This is my main.dart:
class MyApp extends StatelessWidget {
var login = new Login("xxxxxxx", "xxxxxxxxx");
final scaffoldKey = new GlobalKey<ScaffoldState>();
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Fetch Data Example',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: Scaffold(
key: scaffoldKey,
appBar: AppBar(
title: Text('Fetch Data Example'),
),
body: Center(
child: FutureBuilder(
future: login.main(),
builder: (context, snapshot) {
if (snapshot.hasData) {
_showSnackBar(snapshot.data);
return new Container();
} else if (snapshot.hasError) {
return Text("${snapshot.error}");
}
// By default, show a loading spinner
return CircularProgressIndicator();
},
),
),
),
);
}
_showSnackBar(var text) {
scaffoldKey.currentState
.showSnackBar(new SnackBar(content: new Text(text)));
}
}
I'd like to show a SnackBar in FutureBuilder, but when I run, something wrong occurs:
I/flutter ( 8918): ══╡ EXCEPTION CAUGHT BY WIDGETS LIBRARY
╞═══════════════════════════════════════════════════════════
I/flutter ( 8918): The following assertion was thrown building
FutureBuilder<dynamic>(dirty, state:
I/flutter ( 8918): _FutureBuilderState<dynamic>#9d4af):
I/flutter ( 8918): setState() or markNeedsBuild() called during build.
I/flutter ( 8918): This Scaffold widget cannot be marked as needing to
build because the framework is already in the
I/flutter ( 8918): process of building widgets. A widget can be marked
as needing to be built during the build phase
I/flutter ( 8918): only if one of its ancestors is currently building.
This exception is allowed because the framework
I/flutter ( 8918): builds parent widgets before children, which means
a dirty descendant will always be built.
I/flutter ( 8918): Otherwise, the framework might not visit this
widget during this build phase.
I/flutter ( 8918): The widget on which setState() or markNeedsBuild()
was called was:
I/flutter ( 8918): Scaffold-[LabeledGlobalKey<ScaffoldState>#e4a3f]
(state: ScaffoldState#cf908(tickers: tracking 2
I/flutter ( 8918): tickers))
I/flutter ( 8918): The widget which was currently being built when the
offending call was made was:
I/flutter ( 8918): FutureBuilder<dynamic>(dirty, state: _
FutureBuilderState<dynamic>#9d4af)
I/flutter ( 8918):
I/flutter ( 8918): When the exception was thrown, this was the stack:
I/flutter ( 8918): #0 Element.markNeedsBuild.<anonymous closure>
(package:flutter/src/widgets/framework.dart:3472:11)
I/flutter ( 8918): #1 Element.markNeedsBuild
(package:flutter/src/widgets/framework.dart:3498:6)
I/flutter ( 8918): #2 State.setState
(package:flutter/src/widgets/framework.dart:1141:14)
I/flutter ( 8918): #3 ScaffoldState.showSnackBar
(package:flutter/src/material/scaffold.dart:1110:5)
I/flutter ( 8918): #4 MyApp._showSnackBar
I/flutter ( 8918): #5 MyApp.build.<anonymous closure>
I/flutter ( 8918): #6 _FutureBuilderState.build
(package:flutter/src/widgets/async.dart)
I/flutter ( 8918): #7 StatefulElement.build
(package:flutter/src/widgets/framework.dart:3766:27)
I/flutter ( 8918): #8 ComponentElement.performRebuild
(package:flutter/src/widgets/framework.dart:3678:15)
I/flutter ( 8918): #9 Element.rebuild
(package:flutter/src/widgets/framework.dart:3531:5)
I/flutter ( 8918): #10 BuildOwner.buildScope
(package:flutter/src/widgets/framework.dart:2273:33)
I/flutter ( 8918): #11
My purpose is that it shows a loading spinner before it gets the data, and once it got the data, it shows a SnackBar. So what should I do to fix the problem? Login.main is a function that GET data from the database.
FutureBuilder or StreamBuilder should only used to build widgets, not to execute logic like navigation. Use instead initState like
#override
void initState() {
super.initState();
_checkLogin();
}
Future<void> _checkLogin() async {
try {
var data = await login.main();
setState(() {
_waitForLogin = false;
});
_showSnackBar(data);
} catch(e) {
setState(() {
_waitForLogin = false;
_loginErrorMessage = '$e';
});
}
}
bool _isWaitForLogin = true;
String _loginErrorMessage;
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Fetch Data Example',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: Scaffold(
key: scaffoldKey,
appBar: AppBar(
title: Text('Fetch Data Example'),
),
body: Center(
child: _isWaitForLogin ? CircularProgressIndicator() :
(_loginErrorMessage != null ? Text(_loginErrorMessage : Container())
),
),
);
}
The solution that #thought7878 recommended suggests wrapping the breaking logic in WidgetsBinding.instance.addPostFrameCallback((_) { }. For example:
builder: (context, AsyncSnapshot<Object> snapshot) {
if (snapshot.hasError) {
WidgetsBinding.instance.addPostFrameCallback((_) {
Scaffold.of(context)
..hideCurrentSnackBar()
..showSnackBar(
// SnackBar
);
});
return Container();
}
// Rest ...
}
Could you try like this: modify _showSnackBar and pass the context
_showSnackBar(var text, BuildContext context) {
ScaffoldKey.of(context)
.showSnackBar(new SnackBar(content: new Text(text)));
}
Related
I'm learning Flutter by following the relatively recent tutorial (made in February - LINK) and while I wrote the same code as author I'm getting the following exception:
======== Exception caught by widgets library =======================================================
The following FirebaseException was thrown building FutureBuilder<FirebaseApp>(dirty, state: _FutureBuilderState<FirebaseApp>#881ee):
[core/no-app] No Firebase App '[DEFAULT]' has been created - call Firebase.initializeApp()
The relevant error-causing widget was:
FutureBuilder<FirebaseApp> FutureBuilder:file:///C:/Users/Mrky/Desktop/Sve/Faks/10_semestar/mynotes/lib/main.dart:30:13
When the exception was thrown, this was the stack:
#0 MethodChannelFirebase.app (package:firebase_core_platform_interface/src/method_channel/method_channel_firebase.dart:173:5)
#1 Firebase.app (package:firebase_core/src/firebase.dart:53:41)
#2 FirebaseAuth.instance (package:firebase_auth/src/firebase_auth.dart:38:47)
#3 HomePage.build.<anonymous closure> (package:mynotes/main.dart:37:34)
#4 _FutureBuilderState.build (package:flutter/src/widgets/async.dart:615:55)
#5 StatefulElement.build (package:flutter/src/widgets/framework.dart:4919:27)
#6 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:4806:15)
#7 StatefulElement.performRebuild (package:flutter/src/widgets/framework.dart:4977:11)
#8 Element.rebuild (package:flutter/src/widgets/framework.dart:4529:5)
#9 BuildOwner.buildScope (package:flutter/src/widgets/framework.dart:2659:19)
#10 WidgetsBinding.drawFrame (package:flutter/src/widgets/binding.dart:891:21)
#11 RendererBinding._handlePersistentFrameCallback (package:flutter/src/rendering/binding.dart:370:5)
#12 SchedulerBinding._invokeFrameCallback (package:flutter/src/scheduler/binding.dart:1146:15)
#13 SchedulerBinding.handleDrawFrame (package:flutter/src/scheduler/binding.dart:1083:9)
#14 SchedulerBinding._handleDrawFrame (package:flutter/src/scheduler/binding.dart:997:5)
#18 _invoke (dart:ui/hooks.dart:151:10)
#19 PlatformDispatcher._drawFrame (dart:ui/platform_dispatcher.dart:308:5)
#20 _drawFrame (dart:ui/hooks.dart:115:31)
(elided 3 frames from dart:async)
==========================================================================================
The code that I have currently written is:
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/material.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:mynotes/firebase_options.dart';
import 'package:mynotes/views/login_view.dart';
void main() {
WidgetsFlutterBinding.ensureInitialized();
runApp(
MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.green,
),
home: const HomePage(),
),
);
}
class HomePage extends StatelessWidget {
const HomePage({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Home'),
),
body: FutureBuilder(
future: Firebase.initializeApp(
options: DefaultFirebaseOptions.currentPlatform,
),
builder: (context, snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.done:
print(FirebaseAuth.instance.currentUser);
return const Text('Done');
default:
return const Text('Loading...');
}
},
),
);
}
}
As it is visible I have Firebase.initializeApp inside future of FutureBuilder. Also If I comment out the line print(FirebaseAuth.instance.currentUser); the app will show text Done without any problem.
Run flutter clean, then add the dart pub package firebase_core with the command flutter pub add firebase_core. Finally, run flutter pub get to resolve all the dependencies.
Make your void function async and add the following lines inside your void main.
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp(const MyApp());
}
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)
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
This is my code and the error I'm getting in Visual Studio Code. It used to work before, not sure if an update messed it up. I have other Flutter programs running just fine, not sure what the problem is with this one.
// 1) Create a new Flutter App (in this project) and output an AppBar and some text
// below it
// 2) Add a button which changes the text (to any other text of your choice)
// 3) Split the app into three widgets: App, TextControl & Text
import 'dart:math';
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
// void main() => runApp(MyApp());
class MyApp extends StatefulWidget {
#override
State<StatefulWidget> createState() {
return _MyAppState();
}
}
class _MyAppState extends State<MyApp> {
Random _stringIndex = new Random();
var _randomStringIndex;
final _strings = const [
"Hello",
"This is a string",
"This is random",
];
void _changeText() {
setState(() {
_randomStringIndex = _stringIndex.nextInt(_strings.length);
});
print(_strings[_randomStringIndex]);
}
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('My First App'),
),
body: Center(
child: Container(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
RaisedButton(
onPressed: _changeText,
),
Text(
_strings[_randomStringIndex],
),
],
),
),
),
),
);
}
}
Launching lib/main.dart on Android SDK built for x86 in debug mode...
✓ Built build/app/outputs/apk/debug/app-debug.apk. Connecting to VM
Service at ws://127.0.0.1:40605/I0TqLbk5jw4=/ws D/EGL_emulation(
7151): eglMakeCurrent: 0xe1b1a3c0: ver 2 0 (tinfo 0xe1b0f770)
D/eglCodecCommon( 7151): setVertexArrayObject: set vao to 0 (0) 1 0
I/flutter ( 7151): ══╡ EXCEPTION CAUGHT BY WIDGETS LIBRARY
╞═══════════════════════════════════════════════════════════ I/flutter
( 7151): The following ArgumentError was thrown building MyApp(dirty,
state: _MyAppState#202c3): I/flutter ( 7151): Invalid argument(s)
I/flutter ( 7151): I/flutter ( 7151): The relevant error-causing
widget was: I/flutter ( 7151): MyApp I/flutter ( 7151):
package:flutter_assignment/main.dart:11 I/flutter ( 7151): I/flutter (
7151): When the exception was thrown, this was the stack: I/flutter (
7151): #0 List.[] (dart:core-patch/array.dart:169:52) I/flutter
( 7151): #1 _MyAppState.build
package:flutter_assignment/main.dart:57 I/flutter ( 7151): #2
StatefulElement.build package:flutter/…/widgets/framework.dart:4619
I/flutter ( 7151): #3 ComponentElement.performRebuild
package:flutter/…/widgets/framework.dart:4502 I/flutter ( 7151): #4
StatefulElement.performRebuild
package:flutter/…/widgets/framework.dart:4675 I/flutter ( 7151): #5
Element.rebuild package:flutter/…/widgets/framework.dart:4218
I/flutter ( 7151): #6 ComponentElement._firstBuild
package:flutter/…/widgets/framework.dart:4481 I/flutter ( 7151): #7
StatefulElement._firstBuild
package:flutter/…/widgets/framework.dart:4666 I/flutter ( 7151): #8
ComponentElement.mount package:flutter/…/widgets/framework.dart:4476
I/flutter ( 7151): #9 Element.inflateWidget
package:flutter/…/widgets/framework.dart:3446 I/flutter ( 7151): #10
Element.updateChild package:flutter/…/widgets/framework.dart:3214
I/flutter ( 7151): #11 RenderObjectToWidgetElement._rebuild
package:flutter/…/widgets/binding.dart:1148 I/flutter ( 7151): #12
RenderObjectToWidgetElement.mount
package:flutter/…/widgets/binding.dart:1119 I/flutter ( 7151): #13
RenderObjectToWidgetAdapter.attachToRenderTree.
package:flutter/…/widgets/binding.dart:1061 I/flutter ( 7151): #14
BuildOwner.buildScope package:flutter/…/widgets/framework.dart:2607
I/flutter ( 7151): #15
RenderObjectToWidgetAdapter.attachToRenderTree
package:flutter/…/widgets/binding.dart:1060 I/flutter ( 7151): #16
WidgetsBinding.attachRootWidget
package:flutter/…/widgets/binding.dart:941
You can copy paste run full code below
You need to init _randomStringIndex
code snippet
var _randomStringIndex = 0;
working demo
full code
import 'dart:math';
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
// void main() => runApp(MyApp());
class MyApp extends StatefulWidget {
#override
State<StatefulWidget> createState() {
return _MyAppState();
}
}
class _MyAppState extends State<MyApp> {
Random _stringIndex = new Random();
var _randomStringIndex = 0;
final _strings = const [
"Hello",
"This is a string",
"This is random",
];
void _changeText() {
setState(() {
_randomStringIndex = _stringIndex.nextInt(_strings.length);
});
print(_strings[_randomStringIndex]);
}
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('My First App'),
),
body: Center(
child: Container(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
RaisedButton(
onPressed: _changeText,
),
Text(
_strings[_randomStringIndex],
),
],
),
),
),
),
);
}
}
Try to run this command inside your project (terminal):
flutter clean
and then run it again!
I created a flutter project with provider package
Previously it had run well using ScopedModel following this sample project.
I want to implement v3 provider with the same logic,
// main.dart
#override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
ChangeNotifierProvider(builder: (context) => NoteContentModel()),
],
...
home: NoteContentPage(),
),
);
}
// note_content_model.dart
class NoteContentModel extends ChangeNotifier {
final _pageSize = 30;
List<Content> _content = [];
bool _isLoading = false;
int _totalResults;
int _totalPages;
bool _hasMorePages = true;
String _placeName;
bool _isLoadingMore = false;
// .. other setter getter
int getNoteCount() => _content.length;
Future<dynamic> _getData([int page = 1]) async {
var res = await http.get(getUrl(page));
return jsonDecode(res.body);
}
Future getNoteContent([int page = 1]) async {
if (page == 1) {
_isLoading = true;
_content.clear();
} else {
_isLoadingMore = true;
}
notifyListeners();
var responseData = await _getData();
List noteContent = responseData['content'];
noteContent.forEach((content) {
_content.add(Content.fromJson(content));
});
_totalResults = responseData['total_count'];
_totalPages = responseData['page_count'];
if (responseData['page_number'] == totalPages) {
_hasMorePages = false;
}
if (page == 1) {
_isLoading = false;
} else {
_isLoadingMore = false;
}
notifyListeners();
}
}
// note_content_page.dart
class NoteContentPage extends StatefulWidget {
#override
_NoteContentPageState createState() => _NoteContentPageState();
}
class _NoteContentPageState extends State<NoteContentPage> {
int page = 1;
ScrollController controller;
void _scrollListener() {
final NoteContentModel noteModel = Provider.of<NoteContentModel>(context);
if (controller.position.pixels == controller.position.maxScrollExtent) {
if (!noteModel.isLoadingMore && noteModel.hasMorePages) {
page++;
print("Current page: $page");
noteModel.getNoteContent(page);
}
}
}
#override
void initState() {
super.initState();
controller = new ScrollController()..addListener(_scrollListener);
}
#override
void dispose() {
super.dispose();
controller.dispose();
}
#override
Widget build(BuildContext context) {
final NoteContentModel noteModel = Provider.of<NoteContentModel>(context);
// Call initiate first page
noteModel.getNoteContent(page);
return Scaffold(
appBar: AppBar(title: Text("Test")),
body: CustomScrollView(
controller: controller,
slivers: <Widget>[
noteModel.isLoading
? SliverFillRemaining(
child: Center(
child: CircularProgressIndicator(),
),
)
: SliverList(
delegate: SliverChildBuilderDelegate(
(context, index) {
if (index == noteModel.getNoteCount()) {
if (noteModel.hasMorePages) {
print("here1");
return Padding(
padding: const EdgeInsets.symmetric(vertical: 16.0),
child: Center(child: CircularProgressIndicator()),
);
}
return Container();
} else {
return Column(
children: <Widget>[
Padding(
padding: const EdgeInsets.all(16.0),
child: Text(noteModel.content[index].title),
// child: Text("test"),
),
],
);
}
},
childCount: noteModel.getNoteCount(),
),
)
],
),
);
}
}
I always got this message loop markNeedsBuild() called during build end trace error
I/flutter ( 2525): ══╡ EXCEPTION CAUGHT BY FOUNDATION LIBRARY ╞════════════════════════════════════════════════════════
I/flutter ( 2525): The following assertion was thrown while dispatching notifications for NoteContentModel:
I/flutter ( 2525): setState() or markNeedsBuild() called during build.
I/flutter ( 2525): This ListenableProvider<NoteContentModel> widget cannot be marked as needing to build because the
...
I/flutter ( 2525): #0 Element.markNeedsBuild.<anonymous closure> (package:flutter/src/widgets/framework.dart:3670:11)
I/flutter ( 2525): #1 Element.markNeedsBuild (package:flutter/src/widgets/framework.dart:3685:6)
I/flutter ( 2525): #2 State.setState (package:flutter/src/widgets/framework.dart:1161:14)
I/flutter ( 2525): #3 __BuilderListenableDelegate&BuilderStateDelegate&_ListenableDelegateMixin.startListening.<anonymous closure> (package:provider/src/listenable_provider.dart:186:36)
I/flutter ( 2525): #4 ChangeNotifier.notifyListeners (package:flutter/src/foundation/change_notifier.dart:206:21)
I/flutter ( 2525): #5 NoteContentModel.getNoteContent (package:paging_provider/providers/note_content_model.dart:42:5)
I/flutter ( 2525): <asynchronous suspension>
I/flutter ( 2525): #6 _NoteContentPageState.build (package:paging_provider/pages/note_content_page.dart:44:15)
I/flutter ( 2525): #7 StatefulElement.build (package:flutter/src/widgets/framework.dart:4012:27)
I/flutter ( 2525): #8 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:3924:15)
I/flutter ( 2525): #9 Element.rebuild (package:flutter/src/widgets/framework.dart:3721:5)
I/flutter ( 2525): #10 ComponentElement._firstBuild (package:flutter/src/widgets/framework.dart:3907:5)
...
// This trace always loop on app running
I/flutter ( 2525): Another exception was thrown: setState() or markNeedsBuild() called during build....
so that's all, any response will be appreciated.
You must wrap your functions in addPostFrameCallback():
So the call is submitted after the build function finish it work and draw widgets over screen
//Change
noteModel.getNoteContent(page);
//To
WidgetsBinding.instance.addPostFrameCallback((_) => noteModel.getNoteContent(page);
);
Hope this answer being valuable for someone.
I follow the steps of a packpub intermediate course, and the code does not work properly. I need some help to understand the Exception.
The following assertion was thrown while handling a gesture:
type 'List<dynamic>' is not a subtype of type 'List<PopupMenuEntry<Animals>>'
import 'package:flutter/material.dart';
void main() {
runApp(new MaterialApp(
home: new MyApp(),
));
}
class MyApp extends StatefulWidget {
#override
_State createState() => new _State();
}
enum Animals{Cat, Dog, Bird, Lizard, Fish}
class _State extends State<MyApp> {
Animals _selected = Animals.Cat;
String _value = 'Make a Selection';
List _items = new List();
#override
void initState() {
for(Animals animal in Animals.values) {
_items.add(new PopupMenuItem(
child: new Text(_getDisplay(animal),),
value: animal,
));
}
}
void _onSelected(Animals animal){
setState((){
_selected = animal;
_value = 'You Selected ${_getDisplay(animal)}';
});
}
String _getDisplay(Animals animal) {
int index = animal.toString().indexOf('.');
index++;
return animal.toString().substring(index);
}
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text('Name here'),
),
body: new Container(
padding: new EdgeInsets.all(32.0),
child: new Center(
child: new Row(
children: <Widget>[
new Container(
padding: new EdgeInsets.all(5.0),
child: new Text(_value),
),
new PopupMenuButton<Animals>(
child: new Icon(Icons.input),
initialValue: Animals.Cat,
onSelected: _onSelected,
itemBuilder: (BuildContext context) {
return _items;
}
)
],
),
),
),
);
}
}
When I press the PopupMenuButton the list does not appear,
and this is what I get on the console:
I/flutter (10194): ══╡ EXCEPTION CAUGHT BY GESTURE ╞══════════════════════════════════════════════════════════════════
I/flutter (10194): The following assertion was thrown while handling a gesture: I/flutter (10194): type 'List' is not a subtype of type 'List>'
I/flutter (10194): Either the assertion indicates an error in the framework itself, or we should provide substantially
I/flutter (10194): more information in this error message to help you determine and fix the underlying cause.
I/flutter (10194): In either case, please report this assertion by filing a bug on GitHub:
I/flutter (10194):
https://github.com/flutter/flutter/issues/new?template=BUG.md
I/flutter (10194): When the exception was thrown, this was the stack:
I/flutter (10194): #0 _State.build. (package:widget4/main.dart:67:21)
I/flutter (10194): #1 _PopupMenuButtonState.showButtonMenu (package:flutter/src/material/popup_menu.dart:900:21)
I/flutter (10194): #2 _InkResponseState._handleTap (package:flutter/src/material/ink_well.dart:513:14)
I/flutter (10194): #3 _InkResponseState.build. (package:flutter/src/material/ink_well.dart:568:30)
I/flutter (10194): #4 GestureRecognizer.invokeCallback (package:flutter/src/gestures/recognizer.dart:120:24)
I/flutter (10194): #5 TapGestureRecognizer._checkUp (package:flutter/src/gestures/tap.dart:242:9)
I/flutter (10194): #6 TapGestureRecognizer.handlePrimaryPointer (package:flutter/src/gestures/tap.dart:175:7)
I/flutter (10194): #7 PrimaryPointerGestureRecognizer.handleEvent (package:flutter/src/gestures/recognizer.dart:369:9)
I/flutter (10194): #8 PointerRouter._dispatch (package:flutter/src/gestures/pointer_router.dart:73:12)
I/flutter (10194): #9 PointerRouter.route (package:flutter/src/gestures/pointer_router.dart:101:11)
I/flutter (10194): #10
_WidgetsFlutterBinding&BindingBase&GestureBinding.handleEvent (package:flutter/src/gestures/binding.dart:214:19)
I/flutter (10194): #11
_WidgetsFlutterBinding&BindingBase&GestureBinding.dispatchEvent (package:flutter/src/gestures/binding.dart:192:22)
I/flutter (10194): #12
_WidgetsFlutterBinding&BindingBase&GestureBinding._handlePointerEvent (package:flutter/src/gestures/binding.dart:149:7)
I/flutter (10194): #13
_WidgetsFlutterBinding&BindingBase&GestureBinding._flushPointerEventQueue (package:flutter/src/gestures/binding.dart:101:7)
I/flutter (10194): #14
_WidgetsFlutterBinding&BindingBase&GestureBinding._handlePointerDataPacket (package:flutter/src/gestures/binding.dart:85:7)
I/flutter (10194): #18 _invoke1 (dart:ui/hooks.dart:223:10)
I/flutter (10194): #19 _dispatchPointerDataPacket (dart:ui/hooks.dart:144:5)
I/flutter (10194): (elided 3 frames from package dart:async)
I/flutter (10194): Handler: onTap
I/flutter (10194): Recognizer:
I/flutter (10194): TapGestureRecognizer#7540f(debugOwner: GestureDetector, state: accepted, won arena, finalPosition:
I/flutter (10194): Offset(156.5, 334.8), sent tap down) ══════════════════════════════════════════════════════════════
I/flutter (10194): Another exception was thrown: type 'List' is not a subtype of type 'List>'