State Management in Flutter Using Riverpod, StateNotifier, Freezed,

0

 


Let’s build a Todo app,

Hello everyone. In this article, we will create a Todo utility with Riverpod + StateNotifier + Freezed + Domain Driven Design (DDD). When I write this article, additionally I am importing a video associated to this article. So, what you want, you can do, you can each read, or watch it.


First matters first, In this article I will now not provide an explanation for all the things, when you consider that there is a video. I simply create the architecture, and I attempt to train how to create Riverpod with some different things.


Okay, let’s get started. 


Todo App:

Also on my GitHub, I have one extra piece of content, which is rickandmortyapi, and it is associated to once more with Riverpod + StateNotifier + Freezed + Domain Driven Design (DDD). I additionally will write articles and add videos, however this to-do software is necessary to seize how we can use Riverpod. 


Firstly, let’s see our architecture (folders):



Here we have the utility folder, for the enterprise logic. Domain, presentation, perhaps infrastructure, etc. They are defined well HERE (file shape part). 


Note: For this project, here is the pubspec.yaml file (for packages, If you want to use them):


dependencies:

  flutter:

    sdk: flutter

  freezed_annotation: ^2.0.3

  flutter_riverpod: ^1.0.4

  uuid: ^3.0.6

  sizer: ^2.0.15

  

  

dev_dependencies:

  flutter_test:

    sdk: flutter

  freezed: ^2.0.3+1

  build_runner: ^2.1.11


First things first, let’s create a domain folder, it has some models.


import 'package:freezed_annotation/freezed_annotation.dart';


part 'todo_model.freezed.dart';


@freezed

class TodoModel with _$TodoModel {

  const factory TodoModel({

    required String id,

    required String title,

    required bool isTodoCompleted,

  }) = _TodoModel;


  const TodoModel._();


  factory TodoModel.empty() => const TodoModel(

        id: "",

        title: "",

        isTodoCompleted: false,

      );

}


After creating the model, we need to run the below code:


flutter packages pub run build_runner build — delete-conflicting-outputs


…and the blunders will go.


Secondly, we have the software folder’s files.


Let’s additionally create them. When you create this architecture, then you want to begin with developing a country file, due to the fact you can be burdened (It is simply a suggestion).


Here is our todo_state.dart file: 


import 'package:freezed_annotation/freezed_annotation.dart';

import 'package:todo_list_riverpod/domain/todo_model.dart';


part 'todo_state.freezed.dart';


@freezed

class TodoState with _$TodoState {

  factory TodoState({

    required List<TodoModel> todoList,

    required TodoModel todo,

  }) = _TodoState;


  const TodoState._();


  factory TodoState.empty() => TodoState(

        todoList: [],

        todo: TodoModel.empty(),

      );

}


After that, create the todo_event.dart file:


import 'package:freezed_annotation/freezed_annotation.dart';


part 'todo_event.freezed.dart';


@freezed

class TodoEvent with _$TodoEvent {

  const factory TodoEvent.todoTitleChanged({required String text}) = TodoTitleChanged;

  const factory TodoEvent.todoStatusChanged({required String todoId}) = TodoStatusChanged;

  const factory TodoEvent.addTodo() = AddTodo;

  const factory TodoEvent.removeTodo({required String todoId}) = RemoveTodo;

}


After creating the todo state, and todo event, we need to run the below code:


flutter packages pub run build_runner build — delete-conflicting-outputs


The blunders will go.


Now, the closing component is the country notifier section. Our nation is todo, so we can rename it with TodoNotifier.


Moreover, create a todo_notifier.dart file:


import 'package:flutter_riverpod/flutter_riverpod.dart';

import 'package:todo_list_riverpod/application/todo/todo_event.dart';

import 'package:todo_list_riverpod/application/todo/todo_state.dart';

import 'package:todo_list_riverpod/domain/todo_model.dart';

import 'package:uuid/uuid.dart';


class TodoNotifier extends StateNotifier<TodoState> {

  TodoNotifier() : super(TodoState.empty());


  final uuid = const Uuid();


  mapEventsToStates(TodoEvent event) async {

    return event.map(

      todoTitleChanged: (todoTitleChangedEvent) {

        state = state.copyWith(

          todo: TodoModel(

            id: state.todo.id,

            title: todoTitleChangedEvent.text.trimLeft(),

            isTodoCompleted: false,

          ),

        );

      },

      todoStatusChanged: (todoStatusChangedEvent) {

        final selectedTodo = state.todoList.where((element) => element.id == todoStatusChangedEvent.todoId).single;

        final todolist = [...state.todoList];


        todolist[todolist.indexWhere((element) => element.id == selectedTodo.id)] =

            TodoModel(id: selectedTodo.id, title: selectedTodo.title, isTodoCompleted: !selectedTodo.isTodoCompleted);


        state = state.copyWith(todoList: todolist);

      },

      addTodo: (addTodoEvent) {

        final List<TodoModel> todoList = [...state.todoList];

        todoList.add(

          TodoModel(

            id: uuid.v1(),

            title: state.todo.title,

            isTodoCompleted: false,

          ),

        );

        state = state.copyWith(todoList: todoList);

      },

      removeTodo: (removeTodoEvent) {

        final List<TodoModel> todoList = [...state.todoList];

        final todoId = removeTodoEvent.todoId;


        todoList.removeWhere((element) => element.id == todoId);


        state = state.copyWith(todoList: todoList);

      },

    );

  }

}


Here, we do now not have to run construct runner blah blah, we do now not need it.


We have notifier, state, events, and models. Now, we can create our provider, and after that, we can attain these states, events, etc. by using Provider.


In the issuer folder, create todo_provider.dart : 


import 'package:flutter_riverpod/flutter_riverpod.dart';

import 'package:todo_list_riverpod/application/todo/todo_notifier.dart';

import 'package:todo_list_riverpod/application/todo/todo_state.dart';


final todoNotifierProvider = StateNotifierProvider<TodoNotifier, TodoState>(

  (ref) {

    return TodoNotifier();

  },

);


Here we use StateNotifierProvider due to the fact we desire to attain our notifier, and additionally its state. So we created StateNotifierProvider with our TodoNotifier and TodoState. As you can see, it returns TodoNotifier() as well. 

The ultimate issue we want to do, calling our state, ship events, the usage of our issuer due to the fact we are nearly done!


Let’s go the main.dart folder.


If you favor to use Riverpod tree, then you should wrap all the matters with ProviderScope().


Simply, our main.dart file is here: 


import 'package:flutter/material.dart';

import 'package:flutter_riverpod/flutter_riverpod.dart';

import 'package:todo_list_riverpod/presentation/core/app_widget.dart';


void main() {

  runApp(

    const ProviderScope(

      child: AppWidget(),

    ),

  );

}


Next, we can move the presentation folder.


Here is our app_widget (core file):


import 'dart:io';


import 'package:flutter/material.dart';

import 'package:sizer/sizer.dart';

import 'package:todo_list_riverpod/presentation/pages/home_page/home_page.dart';


class AppWidget extends StatelessWidget {

  const AppWidget({Key? key}) : super(key: key);


  @override

  Widget build(BuildContext context) {

    return Sizer(

      builder: (context, orientation, deviceType) {

        return Listener(

          onPointerUp: (_) {

            if (Platform.isIOS) {

              FocusScopeNode currentFocus = FocusScope.of(context);

              if (!currentFocus.hasPrimaryFocus && currentFocus.focusedChild != null) {

                FocusManager.instance.primaryFocus!.unfocus();

              }

            }

          },

          child: const MaterialApp(

            debugShowCheckedModeBanner: false,

            home: HomePage(),

          ),

        );

      },

    );

  }

}


Of course, do not forget to import the right things!


Moreover, we have home_page:


@override

  Widget build(BuildContext context, WidgetRef ref) {

    final todoList = ref.watch(todoNotifierProvider.select((state) => state.todoList));

    final reversedTodoList = List<TodoModel>.from(todoList.reversed);

    .

    .

    .


We have the closing todoList variable. We use ref.watch technique to get our STATE, no longer METHOD. If you favor to attain your notifier, which has your methods, and functions, then you want to use the yourprovider.notifier code. For state, use simply ref.watch(yourProvider). Sometimes pick technique additionally can be useful.


For example, we use the pick method, due to the fact we simply favor to watch todoList, now not all the state.


In the home_page.dart file, we have additionally different parts, such as TodosActionPart and TodosPart sections.


In the TodosActionPart section, we have a button and textfield. We do no longer use the onChanged, due to the fact that we have TextfieldController, and we keep our textual content here. 


 onPressed: () {

            ref.read(todoNotifierProvider.notifier).mapEventsToStates(

                  TodoTitleChanged(text: textfieldController.value.text),

                );

            ref.read(todoNotifierProvider.notifier).mapEventsToStates(

                  const AddTodo(),

                );

            textfieldController.clear();

          },


When we use Riverpod, If we use a button or onTap, or onClick, It does now not matter, we can't use watch. Do now not use it. We do now not hear to anything, we simply click on the button for example, and that’s it. For extra information, go to the riverpod.dev link.


We use provider.notifier to attain notifier. Then, we name our function, which title is mapEventsToState. First, we add the TodoTitleChanged event, and next, we add the AddTodo event.


Post a Comment

0Comments
* Please Don't Spam Here. All the Comments are Reviewed by Admin.
Post a Comment (0)

#buttons=(Accept !) #days=(20)

Our website uses cookies to enhance your experience. Learn More
Accept !