dwyl / flutter-todo-list-tutorial

✅ A detailed example/tutorial building a cross-platform Todo List App using Flutter 🦋
GNU General Public License v2.0
87 stars 8 forks source link

Use provider as state management tool for list of tasks #22

Open SimonLab opened 3 years ago

SimonLab commented 3 years ago

ref: https://github.com/dwyl/flutter-todo-list-tutorial/issues/21#issuecomment-742535367

Provider can be use to make it easier to manage the state of the application, in particular to listen to changes applied to the list of tasks (e.g add a new task to the list, toggle complete/uncomplete task from the list...). So instead of letting the widget managing their own state we can use provide to create one source for the state application which will define how to display the tasks.

see also https://flutter.dev/docs/development/data-and-backend/state-mgmt/simple

SimonLab commented 3 years ago

As an exercise to understand how provides works I've updated the default counter flutter application:

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';

void main() {
  runApp(MyApp());
}

// create counter model extending ChangeNotifier
// to be able to use the notifyListeners function
class CounterModel extends ChangeNotifier {
  int counter = 0;

  String toString() => '$counter';
  // move the increment function which was defined in the widget to the model
  void increment() {
    counter++;
    // notify that the counter value has changed
    notifyListeners();
  }

  // define a reset function 
  void reset() {
    counter = 0;
    notifyListeners();
  }
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
        title: 'Flutter Demo',
        theme: ThemeData(
          primarySwatch: Colors.blue,
        ),
        // create the change notifier widget wihch use the counter model
        home: ChangeNotifierProvider(
            create: (context) => CounterModel(),
            child: MyHomePageProvider(title: 'Flutter Demo Home Page')));
  }
}

// The home page is now a stateless widget as
// all the changes are managed by provider
class MyHomePageProvider extends StatelessWidget {
  MyHomePageProvider({Key key, this.title}) : super(key: key);
  final String title;

  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text(
              'You have pushed the button this many times:',
            ),
            // Create a first consumer of the model changes
            Consumer<CounterModel>(
              builder: (context, counter, child) {
                return Text(
                  '${counter.counter}',
                  style: Theme.of(context).textTheme.headline4,
                );
              },
            ),
            // Use Provider.of to get an instance of the counter model without having to read the counter value itself
            // this avoid rebuilding the button each time the counter valiue change
            RaisedButton(onPressed: Provider.of<CounterModel>(context).reset, child: Text('reset'),)
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        // call the increment function from the counter instance selected via Provider.of
        onPressed: Provider.of<CounterModel>(context, listen: false).increment,
        tooltip: 'Increment',
        child: Icon(Icons.add),
      ),
    );
  }
}

I'm now updating the todolist application to remove the statefull widgets when not necessary and replacing them with provider models.

SimonLab commented 3 years ago

I've converted the todolist widget from stateful to stateless and use provider to manage the state (adding a new task to the list), see the commit: https://github.com/dwyl/flutter-todo-list-tutorial/pull/17/commits/22d39e3f85638ae5dda79eb47afbef66843761af. I've been looking at adding provider to each tasks but I don't think it will be useful in this case and using stateful widgets for the tasks might be easier to manage.

I'm looking at now using a FutureProvider to load the saved tasks from the devices. The async function to get the tasks from the shared preferences need to be call when the state is initialised with provider.