0

I want to get my state from database upon startup. I use provider with ChangeNotivierProvider at my first widget. Code included for clarification.

This is my main method, it has the widget that provides the state i plan to use in the app:

void main() => runApp(
    ChangeNotifierProvider(
            child: MyApp(),
            builder: (context) => StateDao.get(),
    ),
}

My DAO just returns the state from database (i use sembast, but could just as easily be sqflite or Firebase)

Future<State> get() async { 
    // Database logic
}

State is just an object extending ChangeNotifier

class State extends ChangeNotifier {
    // Getters, setters, changeNotifiers etc.
}

This will not work as i cannot call async methods in the builder-method of ChangeNotifierProvider. How, when and where should this be initialized? As i understand it, async calls should not be done in any build methods. I tried overriding didChangeDependencies that provided context-access but i could not get past the async-call in builder method limitation.

jared
  • 473
  • 3
  • 16

1 Answers1

1

You can initialize the value in initState. For example, you can store Future object and use FutureBuilder to build widget when a value is resolved:

  Future<State> futureState;

  @override
  void initState() {
    // Calling async method and storing Future object
    futureState = StateDao.get();
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return FutureBuilder<State>(
      future: futureState,
      builder: (context, snapshot) {
        // No data yet, showing loader
        if (!snapshot.hasData) {
          return CircularProgressIndicator();
        }

        return ChangeNotifierProvider(
          child: MyApp(),
          builder: (context) => snapshot.data,
        );
      },
    );
  }

Another similar approach is to use StreamController and StreamBuilder:

  final StreamController<State> stateStream = StreamController<State>();

  @override
  void initState() {
    // Calling async method and setting callback to add data to the stream
    StateDao.get().then((v) => stateStream.add(v));
    super.initState();
  }

  @override
  void dispose() {
    // Closing the sink
    stateStream.close();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return StreamBuilder<State>(
      stream: stateStream.stream,
      builder: (context, snapshot) {
        // No data yet, showing loader
        if (!snapshot.hasData) {
          return CircularProgressIndicator();
        }

        return ChangeNotifierProvider(
          child: MyApp(),
          builder: (context) => snapshot.data,
        );
      },
    );
  }

If you need more complex architecture, you might want to look at BLoC, which is very popular architecture in Flutter community.

Igor Kharakhordin
  • 9,185
  • 3
  • 40
  • 39