How can I fetch the Flutter Provider state from an rest API once after login?
I suggest you move from futurebuilder to streambuilder then try your provider. I don't like future builder because everytime the widget state changes, the future builder is invoked. The traffic to the server increases. The provider follows the syntax: Provider.of<your_class>(context,listen=false).mymethod. I keep listen=false because I am not doing any broadcast notifications using the provider.
Look at streambuilder and bloc event and data patterns
Kin Pu
Updated on November 27, 2022Comments
-
Kin Pu over 1 year
I would like to know what is the best way to fetch all the Provider state that I need from an API. I am not doing it on my main screen after login because the initState() always will be called when I navigate back from another screen.
Right now, my approach was creating a LoadScreen widget that is only built after login. There I use FutureBuilder to load the state I need and then Navigate to Main. This approach generates an exception but "works". What would be a correct approach?
import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'package:shop/providers/auth.dart'; import 'package:shop/providers/collections.dart'; import 'package:shop/providers/customers.dart'; import 'package:shop/providers/orders.dart'; import 'package:shop/providers/products.dart'; import 'package:shop/providers/recommended_categories.dart'; import 'package:shop/providers/user_data.dart'; import 'package:shop/screens/main_screen.dart'; class LoadScreen extends StatelessWidget { static const routeName = '/load-screen'; //It shouldn't never be used. @override Widget build(BuildContext context) { Future<bool> loadState() async { //Loading all state data from firebase print('Loading States'); try { final authData = await Provider.of<Auth>(context, listen: false).authData; final futures = <Future<dynamic>>[ Provider.of<Products>(context, listen: false) .fetchAndSet(authData), //Products Provider.of<RecommendedCategories>(context, listen: false) .fetchAndSet(authData), Provider.of<Customers>(context, listen: false).fetchAndSet(authData), Provider.of<Orders>(context, listen: false).fetchAndSet(authData), Provider.of<UserData>(context, listen: false).fetchAndSet(authData), Provider.of<Collections>(context, listen: false) .fetchAndSet(authData), ]; print('Created futures list'); final results = await Future.wait(futures); print(results); } catch (error, stacktrace) { print(error); print(stacktrace); throw error; } return true; } return Scaffold( body: FutureBuilder( future: loadState(), builder: (context, snapshot) { if (snapshot.connectionState == ConnectionState.done) { if (snapshot.hasError) { return Center( child: Text('${snapshot.error}'), ); } Navigator.of(context).pushNamed(MainScreen.routeName); return Center( child: Text('Loading...'), ); } else { return Center( child: Text('Loading...'), ); } }, ), ); } }
My main is
import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'package:shop/providers/collections.dart'; import 'package:shop/providers/customers.dart'; import 'package:shop/providers/products.dart'; import 'package:shop/providers/recommended_categories.dart'; import 'package:shop/providers/user_data.dart'; import 'package:shop/screens/add_collection_screen.dart'; import 'package:shop/screens/add_order_screen.dart'; import 'package:shop/screens/load_screen.dart'; import 'package:shop/screens/products_screen.dart'; import 'package:shop/screens/customer_order_screen.dart'; import 'package:shop/screens/edit_customer_screen.dart'; import 'package:shop/screens/main_screen.dart'; //import './screens/products_overview_screen.dart'; import 'screens/cart_screen.dart'; import './screens/product_detail_screen.dart'; import './providers/cart.dart'; import './providers/orders.dart'; import './providers/auth.dart'; import './screens/orders_screen.dart'; import './screens/auth_screen.dart'; import './screens/splash-screen.dart'; import './screens/collections_screen.dart'; // import './screens/collection_products_screen.dart'; void main() => runApp(MyApp()); class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { print('MyApp.build'); return MultiProvider( providers: [ ChangeNotifierProvider( //Recommended approach when a new object is provided. create: (ctx) => Auth(), ), ChangeNotifierProvider<Products>( //Recommended approach when a new object is provided. create: (ctx) => Products(), ), ChangeNotifierProvider( create: (ctx) => Cart(), ), ChangeNotifierProvider<Orders>( create: (ctx) => Orders(), ), ChangeNotifierProvider( create: (ctx) => Customers(), ), ChangeNotifierProvider( create: (ctx) => RecommendedCategories(), ), ChangeNotifierProvider( create: (ctx) => UserData(), ), ChangeNotifierProvider( create: (ctx) => Collections(), ), ], child: Consumer<Auth>( builder: (ctx, auth, _) => MaterialApp( title: 'Shop', theme: ThemeData( primarySwatch: Colors.deepOrange, accentColor: Colors.purple, fontFamily: 'Lato', ), home: auth.canAuth ? LoadScreen() : FutureBuilder( future: auth.tryAutoLogin(), builder: (ctx, authResultSnapshot) => authResultSnapshot.connectionState == ConnectionState.waiting ? SplashScreen() : AuthScreen(), ), routes: { ProductDetailScreen.routeName: (ctx) => ProductDetailScreen(), CartScreen.routeName: (ctx) => CartScreen(), OrdersScreen.routeName: (ctx) => OrdersScreen(), CollectionProductsScreen.routeName: (ctx) => CollectionProductsScreen(), CollectionsScreen.routeName: (ctx) => CollectionsScreen(false), EditCustomerScreen.routeName: (ctx) => EditCustomerScreen(), CustomerOrderScreen.routeName: (ctx) => CustomerOrderScreen(), AddOrderScreen.routeName: (ctx) => AddOrderScreen(), AddCollectionScreen.routeName: (ctx) => AddCollectionScreen(), LoadScreen.routeName: (ctx) => LoadScreen(), MainScreen.routeName: (ctx) => MainScreen(), }, ), ), ); } }
I am getting the following exception
Exception caught by widgets library ═══════════════════════════════════ The following assertion was thrown building FutureBuilder<bool>(dirty, state: _FutureBuilderState<bool>#547fc): setState() or markNeedsBuild() called during build. This Overlay widget cannot be marked as needing to build because the framework is already in the process of building widgets. A widget can be marked as needing to be built during the build phase only if one of its ancestors is currently building. This exception is allowed because the framework builds parent widgets before children, which means a dirty descendant will always be built. Otherwise, the framework might not visit this widget during this build phase. The widget on which setState() or markNeedsBuild() was called was: Overlay-[LabeledGlobalKey<OverlayState>#68f4a] state: OverlayState#77296(tickers: tracking 0 tickers, entries: [OverlayEntry#847ca(opaque: true; maintainState: false), OverlayEntry#7a334(opaque: false; maintainState: true), OverlayEntry#9e3e0(opaque: false; maintainState: false), OverlayEntry#99d1e(opaque: false; maintainState: true)]) The widget which was currently being built when the offending call was made was: FutureBuilder<bool> dirty state: _FutureBuilderState<bool>#547fc The relevant error-causing widget was FutureBuilder<bool> lib/screens/load_screen.dart:48 When the exception was thrown, this was the stack #0 Element.markNeedsBuild.<anonymous closure> package:flutter/…/widgets/framework.dart:4138 #1 Element.markNeedsBuild package:flutter/…/widgets/framework.dart:4153 #2 State.setState package:flutter/…/widgets/framework.dart:1287 #3 OverlayState.rearrange package:flutter/…/widgets/overlay.dart:436 #4 NavigatorState._flushHistoryUpdates package:flutter/…/widgets/navigator.dart:4043 ... ════════════════════════════════════════════════════════════════════════════════ E/flutter ( 5098): [ERROR:flutter/lib/ui/ui_dart_state.cc(186)] Unhandled Exception: 'package:flutter/src/widgets/navigator.dart': Failed assertion: line 2997 pos 18: '!navigator._debugLocked': is not true. E/flutter ( 5098): #0 _AssertionError._doThrowNew (dart:core-patch/errors_patch.dart:46:39) E/flutter ( 5098): #1 _AssertionError._throwNew (dart:core-patch/errors_patch.dart:36:5) E/flutter ( 5098): #2 _RouteEntry.handlePush.<anonymous closure> package:flutter/…/widgets/navigator.dart:2997 E/flutter ( 5098): #3 TickerFuture.whenCompleteOrCancel.thunk package:flutter/…/scheduler/ticker.dart:407 E/flutter ( 5098): #4 _rootRunUnary (dart:async/zone.dart:1362:47) E/flutter ( 5098): #5 _CustomZone.runUnary (dart:async/zone.dart:1265:19) E/flutter ( 5098): #6 _FutureListener.handleValue (dart:async/future_impl.dart:152:18) E/flutter ( 5098): #7 Future._propagateToListeners.handleValueCallback (dart:async/future_impl.dart:704:45) E/flutter ( 5098): #8 Future._propagateToListeners (dart:async/future_impl.dart:733:32) E/flutter ( 5098): #9 Future._completeWithValue (dart:async/future_impl.dart:539:5) E/flutter ( 5098): #10 Future._asyncCompleteWithValue.<anonymous closure> (dart:async/future_impl.dart:577:7) E/flutter ( 5098): #11 _rootRun (dart:async/zone.dart:1354:13) E/flutter ( 5098): #12 _CustomZone.run (dart:async/zone.dart:1258:19) E/flutter ( 5098): #13 _CustomZone.runGuarded (dart:async/zone.dart:1162:7) E/flutter ( 5098): #14 _CustomZone.bindCallbackGuarded.<anonymous closure> (dart:async/zone.dart:1202:23) E/flutter ( 5098): #15 _microtaskLoop (dart:async/schedule_microtask.dart:40:21) E/flutter ( 5098): #16 _startMicrotaskLoop (dart:async/schedule_microtask.dart:49:5)