DEV Community

Cover image for A Flutter Todo App using AWS Amplify & GetX
Offline Programmer
Offline Programmer

Posted on • Updated on

A Flutter Todo App using AWS Amplify & GetX

AWSAmplify DataStore enables the developers to add offline capabilities to their apps with just a few lines of code. It provides an on-device data storage service to read, write and observe data (online & offline mode) and sync it to a cloud database (DynamoDB).

Amplify use GraphQL for the data modeling, and it creates the models in the native language of your framework (JS, Java, Swift, Dart, etc.). This post will use the DataStore to create a Todo App where the user can create, update & delete tasks.

GetX is a micro-framework that combines State Management, Dependency Injection, Route Management, and other valuable utilities (e.g., internationalization, theming, validation, etc.). It is one of the most popular state management packages.

We used the Admin UI to create the backend and enable authentication

Screen Shot 2021-05-23 at 7.07.05 PM

Create the Flutter App and run the command below to pull the Amplify backend




amplify pull --appId <Your_appId> --envName staging



Enter fullscreen mode Exit fullscreen mode

Follow the instructions describedhere to add GraphQL API. Once completed, you will get the following in your App.

Screen Shot 2021-05-23 at 7.28.03 PM

We will need two models (Users & ToDo), update the file amplify/backend/{api_name}/schema.graphql as below




type Todo @model {
  id: ID!
  name: String!
  createdAt: AWSTimestamp
  updatedAt: AWSTimestamp
  isDone: Boolean
  userId: String
}

type Users @model {
  id: ID!
  username: String
  email: AWSEmail
  isVerified: Boolean
  createdAt: AWSTimestamp
}



Enter fullscreen mode Exit fullscreen mode

The schema is ready; run the Amplify CLI command below to generate the models.




amplify codegen models



Enter fullscreen mode Exit fullscreen mode

The models will be created as below

Screen Shot 2021-05-23 at 7.28.23 PM

We will use three GetxControllers:

AuthController: This will handle the authentication operations by integrating with AuthService, which will use Amplify to authenticate the user. Once the user is signed in, we will create a user datastore record using DataStoreService. here we will also determine what page to display to the user based on the auth status



....

handleAuthChanged(isSignedIn) {
    if (!isSignedIn) {
      Get.offAllNamed("/signin");
    } else {
      Get.offAllNamed("/home");
    }
  }

...




Enter fullscreen mode Exit fullscreen mode

Check my previous post to learn more about Amplify Authentication

UserController: We will use it to get the user record from the DataStore.



....

  Future<void> getCurrUser() async {
    AuthUser authUser = await _authService.getCurrentUser();
    currentUser.value = await _datastoreService.getUser(authUser.userId);
    print(currentUser.value);
  }

...




Enter fullscreen mode Exit fullscreen mode

TodoController: This is for creating\deleting\updating the tasks from the DataStore.



....

  Future<void> getTodos() async {
    AuthUser _authUser = await _authService.getCurrentUser();
    List<Todo> _list = await _datastoreService.getTodos(_authUser.userId);
    todoList.addAllIf(_list != null, _list);
  }

  Future<void> addTodo() async {
    AuthUser _authUser = await _authService.getCurrentUser();
    Todo todo = new Todo(
        name: todoTitleController.text.toString(),
        isDone: false,
        createdAt: TemporalTimestamp.now(),
        updatedAt: TemporalTimestamp.now(),
        userId: _authUser.userId);
    await _datastoreService.saveTodo(todo);
    todoList.add(todo);
  }

  Future<void> removeTodo(Todo todo) async {
    await _datastoreService.removeTodo(todo);
    todoList.remove(todo);
  }

  Future<void> setToDoDone(Todo todo, bool newValue) async {
    await _datastoreService.setToDoDone(todo, newValue);
    todoList[todoList.indexWhere((element) => element.id == todo.id)] =
        todo.copyWith(isDone: newValue);
  }

...



Enter fullscreen mode Exit fullscreen mode

In the HomePage, the user will be able to create and manage the Todo tasks. Here we will use the power of GetX and its reactive state manager to enable the following features:

Displaying the user email



....

appBar: AppBar(
          title: GetX<UserController>(
            initState: (_) async {
              await Get.find<UserController>().getCurrUser();
            },
            builder: (_) {
              return Text(controller.user?.email ?? '',
                  style: TextStyle(fontSize: 12));
            },
          ),

...




Enter fullscreen mode Exit fullscreen mode

Displaying the todo tasks



....

    Obx(
        () => Expanded(
            child: ListView.builder(
                itemCount: _todocontroller.todoList.length,
                itemBuilder: (_, index) {
                    return TodoCard(todo: _todocontroller.todoList[index]);
                },
                ),
            ),
        ),

...



Enter fullscreen mode Exit fullscreen mode

We will use a Get.dialog to add a new Todo task



....

        floatingActionButton: FloatingActionButton(
          onPressed: () {
            Get.dialog(AlertDialog(
              title: Text('Add Todo here'),
              content: TextFormField(
                controller: _todocontroller.todoTitleController,
                decoration: InputDecoration(hintText: "Title"),
              ),
              actions: [
                TextButton(
                  child: Text('OK'),
                  onPressed: () async {
                    if (_todocontroller.todoTitleController.text != "") {
                      await _todocontroller.addTodo();
                      _todocontroller.todoTitleController.clear();
                    }
                    Get.back();
                  },
                ),
              ],
            ));
          },
          child: Icon(Icons.add),
        ),

...



Enter fullscreen mode Exit fullscreen mode

We will show a checkbox for each task; the user can tab it to flag it as done. Dismissing the task will delete it from the DataStore.



....

  @override
  Widget build(BuildContext context) {
    TodoController _todocontroller = Get.find();
    return _buildContent(_todocontroller);
  }

  Dismissible _buildContent(TodoController _todocontroller) {
    return Dismissible(
      direction: DismissDirection.startToEnd,
      onDismissed: (DismissDirection direction) {
        if (direction == DismissDirection.startToEnd) {
          _todocontroller.removeTodo(todo);
        }
      },
      background: Container(
        alignment: Alignment.centerLeft,
        color: Colors.red,
        child: Padding(
          padding: const EdgeInsets.all(8.0),
          child: Icon(
            Icons.delete,
            color: Colors.white,
          ),
        ),
      ),
      key: ValueKey(todo.id),
      child: _buildCard(_todocontroller),
    );
  }

  Card _buildCard(TodoController _todocontroller) {
    return Card(
      margin: EdgeInsets.symmetric(horizontal: 10, vertical: 5),
      child: ListTile(
        leading: Checkbox(
          value: todo.isDone,
          onChanged: (newValue) {
            _todocontroller.setToDoDone(todo, newValue);
          },
        ),
        title: Text(
          todo.name,
          style: TextStyle(
            color: Colors.black,
            fontSize: 15,
            fontWeight: FontWeight.bold,
          ),
        ),
        subtitle: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Text(
              DateFormat.yMMMMEEEEd().format(
                  DateTime.fromMillisecondsSinceEpoch(
                      todo.createdAt.toSeconds() * 1000)),
              style: TextStyle(
                color: Colors.grey,
                fontSize: 10,
              ),
            ),
          ],
        ),
      ),
    );
  }

...



Enter fullscreen mode Exit fullscreen mode

Check the code on github

A Flutter Todo App using AWS Amplify & GetX

A simple To-Do List app created using AWS Amplify and GetX.

With this project you will learn how to:

  1. Use AWSAmplify Auth.
  2. Use AWSAmplify DataStore.
  3. Manage the state of the application using GetXController.
    1. Make the UI reactive efficiently, rebuilding only the necessary widgets.

YouTube video demo here:

A Flutter Todo App using AWS Amplify & GetX






Follow me on Twitter for more tips about #coding, #learning, #technology...etc.

Check my Apps on Google Play

Cover image Kelly Sikkema on Unsplash

Top comments (0)