1

I am at the first few screens of designing my flutter application, currently using setState to manage the state. Well, I need to shift from this and use provider instead.

Below code is sample for the my account screen, where users can change their profile information. Below is what it does.

  1. Load user data in init
  2. Update user data on button press

I am thinking of better managing the state, because I need to display waiting message when the data is being saved and save success message when data saved. I am already using Loading Data message at init data load.

Below is my code

AccountPage

    import 'package:flutter/material.dart';
import 'package:xxx/models/company.dart';
import 'package:xxx/models/user.dart';
import 'package:xxx/services/user/user_impl.dart';
import 'package:xxx/services/user/user_interface.dart';

class AccTest extends StatelessWidget {

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("My App"),
      ),
      body: AccTestForm(),
    );
  }
}

class AccTestForm extends StatefulWidget {
  @override
  State<StatefulWidget> createState() {
    return AccTestState();
  }
}

class AccTestState extends State<AccTestForm> {
  final _formKey = GlobalKey<FormState>();

  TextEditingController nameController = TextEditingController();

  UserInterface userinterface = UserImpl();

  int userID = 0;
  int companyID = 0;

  @override
  Widget build(BuildContext context) {
    /**
     * here i am checking for `userID` which was set via `setState`
     */
    if (userID > 0) {
      return ListView(
        children: <Widget>[
          Container(
            child: Text(
              "This is your account section. You can update and change information as you prefer.",
              style: Theme.of(context).textTheme.subhead,
            ),
            margin: EdgeInsets.only(left: 10, right: 10, top: 30),
          ),
          Container(
            child: FutureBuilder(
              future: userinterface.getUserWithCompanyInfo(userID),
              builder: (BuildContext context, AsyncSnapshot snapshot) {
                if (snapshot.connectionState == ConnectionState.done &&
                    snapshot != null) {
                  nameController.text = snapshot.data.name;

                  companyID = snapshot.data.company.idcompany;

                  print("BUILD FORM PRINTED");

                  return _buildForm();
                } else {
                  return Center(
                    child: Container(
                      child: Column(
                        mainAxisAlignment: MainAxisAlignment.center,
                        crossAxisAlignment: CrossAxisAlignment.center,
                        children: <Widget>[
                          CircularProgressIndicator(),
                          Container(
                            height: 10,
                          ),
                          Text(
                            "Loading Data... Please Wait",
                            style: Theme.of(context).textTheme.body1,
                          )
                        ],
                      ),
                    ),
                  );
                }
              },
            ),
            margin: EdgeInsets.only(left: 10, right: 10, top: 30),
          )
        ],
      );
    } else {
      return Center(
        child: Container(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            crossAxisAlignment: CrossAxisAlignment.center,
            children: <Widget>[
              CircularProgressIndicator(),
              Container(
                height: 10,
              ),
              Text(
                "Loading Data... Please Wait",
                style: Theme.of(context).textTheme.body1,
              )
            ],
          ),
        ),
      );
    }
  }

  Widget _buildForm() {
    return Form(
      key: _formKey,
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: <Widget>[
          Container(
              margin: EdgeInsets.only(left: 8, bottom: 5),
              child: Text(
                "Name",
                style: Theme.of(context).textTheme.subtitle,
              )),
          Row(
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              Expanded(
                  child: TextFormField(
                  controller: nameController,
                  keyboardType: TextInputType.text,
                ),)
            ],
          ),
          Container(
            margin: EdgeInsets.only(top: 40, left: 20, right: 20, bottom: 20),
            child: RaisedButton(
                    color: Color.fromRGBO(0, 72, 128, 100),
                    textColor: Colors.white,
                    child: Text(
                      "Save Information",
                      style: Theme.of(context).textTheme.button,
                    ),
                    onPressed: () {
                      if (_formKey.currentState.validate()) {

                        String saveState = null;

                        /**
                         * Updating the `user`
                         */
                        Company company = Company(idcompany: companyID);

                        User user = User(
                          iduser: userID,
                          name: nameController.text,
                          company: company,
                          phone: "something",
                          email: "test@test.com",
                          companyDesignation: "something",
                        );

                        UserInterface userInterface = UserImpl();


                        userInterface.updateUser(user).then((val) {
                          print("User Update: " + val);

                           Scaffold.of(context).showSnackBar(
                              SnackBar(content: Text("ssdsd")));


                        });
                      }
                    },
                  ),
          )
        ],
      ),
    );
  }

  @override
  void initState() {
    super.initState();

  /**
   * Here i get user information by email, then assign them to `userID` global variable
   */
    userinterface.getUserByEmail("test@test.com").then((val) {
      setState(() {
        userID = val.iduser;
      });

      print("user id: " + val.iduser.toString());
    });
  }
}

UserImpl

import 'package:flutter/material.dart';
import 'package:xxx/models/user.dart';
import 'package:xxx/services/nav_links.dart';
import 'package:xxx/services/user/user_interface.dart';
import 'package:http/http.dart' as http;
import 'dart:convert' as convert;

class UserImpl implements UserInterface 
{
  NavLinks navLinks = NavLinks();

  /**
   * Get user with company information
   */
  @override
  Future <User> getUserWithCompanyInfo(int userID) async {
    var data = await http.get(navLinks.getUserWithCompanyInfo(userID));

    if (data.statusCode == 200) {
      return User.fromJson(convert.json.decode(data.body));
    } else {
      throw Exception('Failed to load post');
    }
  }

  @override
  Future<User> getUserByEmail(String email)  async{
    var data = await http.get(navLinks.getUserByEmail(email));

    if (data.statusCode == 200) {
      return User.fromJson(convert.json.decode(data.body));
    } else {
      throw Exception('Failed to load post');
    }
  }

  Future<String> updateUser(User body) async {
    print("BODY: " + body.toJson().toString());

    return http.post(navLinks.updateUser(), body: convert.json.encode(body.toJson()), headers: {
      "Accept": "application/json",
      "content-type": "application/json"
    }).then((http.Response response) {
      final int statusCode = response.statusCode;

      print("RESPONSE: " + response.body);
      print("STATUS CODE: " + statusCode.toString());

      if (statusCode < 200 || statusCode > 400 || response.body == null) {
        throw new Exception("Error while fetching data");
      } else {
        return response.body;
      }
    });
  }

}

I feel very confused when trying to apply provider into this, because I have to deal with couple of Future and its returning data.

Any support on converting this to provider please?

PeakGen
  • 21,894
  • 86
  • 261
  • 463
  • You can start off by using [StreamProvider](https://pub.dev/documentation/provider/latest/provider/StreamProvider-class.html) and [FutureProvider](https://pub.dev/documentation/provider/latest/provider/FutureProvider-class.html). Here's an [example of using FutureProvider](https://github.com/rrousselGit/provider#i-use-changenotifier-and-i-have-an-exception-when-i-update-it-what-happens) – om-ha Feb 28 '20 at 19:50
  • FWIW, use [Consumer<>](https://github.com/rrousselGit/provider#my-widget-rebuilds-too-often-what-can-i-do) whenever possible. And when using `Provider.of` never use `listen` flag with the (default) true value when outside widget tree e.g. `init`. See my [answer here](https://stackoverflow.com/a/59900307/10830091) for more helpful information on the latter. – om-ha Feb 28 '20 at 19:54
  • Hope that was helpful. Sorry I'm not providing an answer as a Provider-rewrite of your question. – om-ha Feb 28 '20 at 19:56
  • One last thing, if one Provider depends on the other, you'll have to use [MultiProvider](https://github.com/rrousselGit/provider#multiprovider). – om-ha Feb 28 '20 at 19:57

0 Answers0