Reputation: 1492
I would like to show a widget on the screen as quickly as possible when my app launches. After the widget has been shown to the user, I'd like to do some app initialization (e.g. set up a database) before leaving the launch screen by pushing another screen. I cannot figure out where to put my app initialization code. Something analogous to viewDidAppear
on iOS.
This is what I tried based on initState
.
class Launch extends StatefulWidget {
@override
_LaunchState createState() {
return _LaunchState();
}
}
class _LaunchState extends State<Launch> {
@override
Widget build(final BuildContext context) {
print('LaunchState build start');
final Widget w = Center(
child: Text('Launching...'),
);
print('LaunchState build end');
return w;
}
@override
void initState() {
print('LaunchState initState start');
super.initState();
print('LaunchState initState middle');
_appInitialization();
print('LaunchState initState end');
}
void _appInitialization() {
print('LaunchState _appInitialization');
}
}
The output was
flutter: LaunchState initState start
flutter: LaunchState initState middle
flutter: LaunchState _appInitialization
flutter: LaunchState initState end
flutter: LaunchState build start
flutter: LaunchState build end
The output I think I want to see is
flutter: LaunchState build start
flutter: LaunchState build end
flutter: LaunchState initState start
flutter: LaunchState initState middle
flutter: LaunchState _appInitialization
flutter: LaunchState initState end
Maybe I'm going about this in a very un-flutter way. If there is a completely different approach, I'm all ears.
SOLUTION
Thanks to Mangaldeep Pannu's answer, it appears I'm missing async
.
class Launch extends StatefulWidget {
@override
_LaunchState createState() {
return _LaunchState();
}
}
class _LaunchState extends State<Launch> {
@override
Widget build(final BuildContext context) {
print('LaunchState build start');
final Widget w = Center(
child: Text('Launching...'),
);
print('LaunchState build end');
return w;
}
@override
void initState() {
print('LaunchState initState start');
super.initState();
print('LaunchState initState middle');
_appInitialization(); // no await
print('LaunchState initState end');
}
void _appInitialization() async {
print('LaunchState _appInitialization begin');
// simulate some time consuming initialization task
await Future.delayed(Duration(seconds: 5));
print('LaunchState _appInitialization middle');
Navigator.push(...);
print('LaunchState _appInitialization end');
}
}
The results are
flutter: LaunchState initState start
flutter: LaunchState initState middle
flutter: LaunchState _appInitialization begin
flutter: LaunchState initState end
flutter: LaunchState build start
flutter: LaunchState build end
[5 second pause]
flutter: LaunchState _appInitialization middle
flutter: LaunchState _appInitialization end
Upvotes: 8
Views: 7843
Reputation: 3987
In your first widget that opens when the app starts
@override
void initState() {
onStart();
}
void onStart() async {
await loadData(); //load your data here
Navigator.push(); //push to next screen
}
onStart
will load the data asynchronously.
And when the data gets loaded push to next screen.
Upvotes: 9
Reputation: 5763
Well we can handle this with BLoC.
create a app_bloc.dart
file and have AppBloc
class
as shown bellow in it:
import 'dart:async';
final appBloc = AppBloc();
enum AppEvent{
onStart, onAppInitialized, onStop
}
class AppBloc {
final _appEventController = StreamController<AppEvent>.broadcast();
Stream<AppEvent> get appEventsStream => _appEventController.stream;
dispatch(AppEvent event) {
switch(event) {
case AppEvent.onStart:
_initializeApp();
_sinkEvent(AppEvent.onStart);
break;
case AppEvent.onStop:
_dispose();
_sinkEvent(AppEvent.onStop);
break;
case AppEvent.onAppInitialized:
_sinkEvent(AppEvent.onAppInitialized);
break;
}
}
void _sinkEvent(AppEvent appEvent) => _appEventController.sink.add(appEvent);
_dispose() {
_appEventController.close();
}
void _initializeApp() async {
await DBProvider.db.initDB();
dispatch(AppEvent.onAppInitialized); // will execute when all initializations are complete,
}
}
create a database.dart
file and have your DBProvider
class
in it:
class DBProvider {
static final DBProvider _instance = new DBProvider._internal();
static final db = DBProvider();
factory DBProvider() {
return _instance;
}
DBProvider._internal();
initDB() async {
// todo initialize your database here.
}
}
You should have your main file something like this:
import 'package:flutter/material.dart';
import 'package:st_overflow/app_bloc.dart';
void main() => runApp(App());
class App extends StatefulWidget {
App() {
appBloc.dispatch(AppEvent.onStart);
}
@override
_AppState createState() => _AppState();
}
class _AppState extends State<App> {
@override
Widget build(BuildContext context) {
return Launch();
}
@override
void dispose() {
appBloc.dispatch(AppEvent.onStop);
super.dispose();
}
}
and in your Launch
Widget
consume the onAppInitialize event using StreamBuilder like this:
StreamBuilder(
stream: appBloc.appEventsStream,
builder: (context, snapshot){
switch (snapshot.data) {
case AppEvent.onAppInitialized:
// remove your Launch widget and show Screen 1 of your app (may be dashboard or something).
break;
}
},
);
Upvotes: 6