The getter 'navigator' was called on null - flutter

Since this morning I have been getting this error, after looking at the stack trace, I have traced the error back to the runApp method, I don't understand
at all, what the problem is, any help would be appreciated :) This is the exception: The getter 'navigator' was called on null
Here is my main.dart file
import 'dart:async';
import 'package:firebase_analytics/firebase_analytics.dart';
import 'package:firebase_analytics/observer.dart';
import 'package:flutter/material.dart';
import 'package:savings/utils/analytics.dart';
import 'package:savings/utils/colors.dart';
import 'package:savings/pages/goals_page.dart';
import 'package:savings/utils/logic.dart' as AppLogic;
import 'package:savings/utils/models.dart' as models;
import 'package:savings/utils/variables.dart';
import 'package:flutter/services.dart';
void main() => runApp(new SavingsApp());
class SavingsApp extends StatefulWidget {
#override
_SavingsAppState createState() => new _SavingsAppState();
}
class _SavingsAppState extends State<SavingsApp> {
#override
void initState() {
initAppData();
super.initState();
}
#override
Widget build(BuildContext context) {
return new MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Savings',
color: primaryThemeColor,
home: GoalsPage(),
navigatorObservers: <NavigatorObserver>[analyticsObserver],
theme: new ThemeData(
primaryColor: primaryThemeColor,
accentColor: accentThemeColor,
fontFamily: "Proxima Nova",
),
);
}
#override
void dispose() {
//Good practice to close the DB
AppLogic.DatabaseInterface.closeDB();
//Handle orientation
SystemChrome.setPreferredOrientations([
DeviceOrientation.landscapeRight,
DeviceOrientation.landscapeLeft,
DeviceOrientation.portraitUp,
DeviceOrientation.portraitDown,
]);
super.dispose();
}
Future<Null> initAppData() async {
//Database
_initDBData();
//Analytics
analytics = new FirebaseAnalytics();
AnalyticsInterface.logAppOpen();
analyticsObserver = new FirebaseAnalyticsObserver(analytics: analytics);
}
Future<Null> _initDBData() async {
//Orientation
SystemChrome.setPreferredOrientations([
DeviceOrientation.portraitUp,
]);
//Retrieve database from the DB interface
appDatabase = await AppLogic.DatabaseInterface.getDB();
//Log output
print("DatabaseInterface: DatabaseInitiated");
//Get a local version of the lists after the DB has been initiated
List<models.Goal> _goalsList =
await AppLogic.DatabaseInterface.getAllGoalsFromDB();
List<models.Transaction> _transactionsList =
await AppLogic.DatabaseInterface.getAllTransactionsFromDB();
//Call setState and update the global lists (which are used in the whole app) from the local ones
setState(() {
allGoalsList = _goalsList;
allTransactionsList = _transactionsList;
});
}
}
Logcat
The following NoSuchMethodError was thrown building
DefaultTextStyle(debugLabel: fallback style;
I/flutter ( 4211): consider putting your text in a Material, inherit: true,
color: Color(0xd0ff0000), family:
I/flutter ( 4211): monospace, size: 48.0, weight: 900, decoration: double
Color(0xffffff00) TextDecoration.underline,
I/flutter ( 4211): softWrap: wrapping at box width, overflow: clip):
I/flutter ( 4211): The getter 'navigator' was called on null.
I/flutter ( 4211): Receiver: null
I/flutter ( 4211): Tried calling: navigator
I/flutter ( 4211):
I/flutter ( 4211): When the exception was thrown, this was the stack:
I/flutter ( 4211): #0 Object.noSuchMethod
(dart:core/runtime/libobject_patch.dart:46:5)
I/flutter ( 4211): #1 NavigatorState.initState
(package:flutter/src/widgets/navigator.dart:1303:23)
I/flutter ( 4211): #2 StatefulElement._firstBuild
(package:flutter/src/widgets/framework.dart:3751:58)
I/flutter ( 4211): #3 ComponentElement.mount
(package:flutter/src/widgets/framework.dart:3617:5)
...
...
I/flutter ( 4211): #104 runApp (package:flutter/src/widgets/binding.dart:704:7)
I/flutter ( 4211): #105 main (file:///C:/Coding/savings/lib/main.dart:13:16)
I/flutter ( 4211): #106 _startIsolate.<anonymous closure (dart:isolate/runtime/libisolate_patch.dart:279:19)
I/flutter ( 4211): #107 _RawReceivePortImpl._handleMessage (dart:isolate/runtime/libisolate_patch.dart:165:12)

You are getting this error because, the navigatorObserver instance "analyticsObserver" you are passing in your navigationObservers array is being instantiated in an async function, means, possibly when the build function is running for the first time, that time your "analyticsObserver" is null.
Try adding the following condition while assigning your navigationObservers array
navigatorObservers: (null == analyticsObserver)? [] : <NavigatorObserver>[analyticsObserver],
Or simply return circular progress indicator while analyticsObserver is null.

Related

keep getting "Null check operator used on a null value" error. i've checked my code 100 times

keep getting "Null check operator used on a null value" error
actually im trying to use providers to get values from database and use em in my app
but im stuck at this i checked my code in every single way but still i keep getting this error
:lib\providers\user_provider.dart
import 'package:flutter/widgets.dart';
import 'package:test_app/models/user.dart';
import 'package:test_app/resources/auth_methods.dart';
class UserProvider with ChangeNotifier {
User? _user;
final AuthMethods _authMethods = AuthMethods();
User get getUser => _user!;
Future<void> refreshUser() async {
User user = await _authMethods.getUserDetails();
_user = user;
notifyListeners();
}
}
lib\pages\home.dart
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:test_app/providers/user_provider.dart';
import 'package:test_app/models/user.dart' as model;
// import 'dart:html';
class Home extends StatefulWidget {
const Home({Key? key}) : super(key: key);
#override
State<Home> createState() => _HomeState();
}
class _HomeState extends State<Home> {
#override
void initState() {
super.initState();
addData();
}
addData() async {
UserProvider _userProvider =
Provider.of<UserProvider>(context, listen: false);
await _userProvider.refreshUser();
}
#override
Widget build(BuildContext context) {
model.User user = Provider.of<UserProvider>(context).getUser;
return Scaffold(
body: Center(
child: Text("this is homepage"),
),
);
}
}
debug snippet
══╡ EXCEPTION CAUGHT BY WIDGETS LIBRARY ╞═══════════════════════════════════════════════════════════
The following _CastError was thrown building Home(dirty, dependencies:
[_InheritedProviderScope<UserProvider?>], state: _HomeState#17895):
Null check operator used on a null value
The relevant error-causing widget was:
Home Home:file:///C:/Users/abc/Desktop/flutter/test_app/lib/main.dart:53:34
When the exception was thrown, this was the stack:
#0 UserProvider.getUser (package:test_app/providers/user_provider.dart:9:28)
#1 _HomeState.build (package:test_app/pages/home.dart:36:59)
#2 StatefulElement.build (package:flutter/src/widgets/framework.dart:4870:27)
#3 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:4754:15)
#4 StatefulElement.performRebuild (package:flutter/src/widgets/framework.dart:4928:11)
#5 Element.rebuild (package:flutter/src/widgets/framework.dart:4477:5)
#6 ComponentElement._firstBuild (package:flutter/src/widgets/framework.dart:4735:5)
#7 StatefulElement._firstBuild (package:flutter/src/widgets/framework.dart:4919:11)
#8 ComponentElement.mount (package:flutter/src/widgets/framework.dart:4729:5)
#9 Element.inflateWidget (package:flutter/src/widgets/framework.dart:3790:14)
#10 Element.updateChild (package:flutter/src/widgets/framework.dart:3524:20)
#11 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:4780:16)
#12 StatefulElement.performRebuild (package:flutter/src/widgets/framework.dart:4928:11)
#13 Element.rebuild (package:flutter/src/widgets/framework.dart:4477:5)
#14 BuildOwner.buildScope (package:flutter/src/widgets/framework.dart:2659:19)
#15 WidgetsBinding.drawFrame (package:flutter/src/widgets/binding.dart:882:21)
#16 RendererBinding._handlePersistentFrameCallback (package:flutter/src/rendering/binding.dart:363:5)
#17 SchedulerBinding._invokeFrameCallback (package:flutter/src/scheduler/binding.dart:1144:15)
#18 SchedulerBinding.handleDrawFrame (package:flutter/src/scheduler/binding.dart:1081:9)
#19 SchedulerBinding.scheduleWarmUpFrame.<anonymous closure> (package:flutter/src/scheduler/binding.dart:862:7)
(elided 11 frames from class _RawReceivePortImpl, class _Timer, dart:async, and dart:async-patch)
idk what the problem actually im new to flutter and dart
i've using python for my whole life
refreshUser is a future and it sets _user but because you're calling getUser IMMEDIATELY build method, which basically returns _user as a non-null value, the error would be thrown.
Try this:
class UserProvider with ChangeNotifier {
User? _user;
final AuthMethods _authMethods = AuthMethods();
User? get getUser => _user;
Future<void> refreshUser() async {
User user = await _authMethods.getUserDetails();
_user = user;
notifyListeners();
}
}
And your _HomeState:
class _HomeState extends State<Home> {
#override
void initState() {
super.initState();
addData();
}
addData() async {
UserProvider _userProvider =
Provider.of<UserProvider>(context, listen: false);
await _userProvider.refreshUser();
}
#override
Widget build(BuildContext context) {
model.User? user = Provider.of<UserProvider>(context).getUser;
return Scaffold(
body: Center(
child: Text("this is homepage"),
),
);
}
}
In User_Provider.dart,
instead of doing:
User get getUser => _user!;
Do this:
User? get getUser => _user;

I am getting error like this Unhandled Exception: Looking up a deactivated widget's ancestor is unsafe

What I trying to do here is getting data from the 000webhost.com and passing that to my model class. That model class is being passed to the provider. Now when I try to display the information in my screen i am getting error.
In this case i have a model class called StudentData.
class StudentData {
final String rollNumber;
final String firstName;
final String lastName;
StudentData({
this.rollNumber,
this.firstName,
this.lastName,
});
}
Here I am fetching data using http package from internet.
And passing the decoded data to the StudentData class and passing that to my data_provider
import 'package:http/http.dart' as http;
void getStudentData(String currentEmail, BuildContext context) async {
final _url = 'https://aaa.000webhostapp.com/getStudentData.php';
final response = await http.post(_url, body: {'email': currentEmail});
var data = response.body;
final decodedData = jsonDecode(data);
final myRollNumber = decodedData['roll_number'];
final myFirstName = decodedData['first_name'];
final myLastName = decodedData['last_name'];
final myStudentData = StudentData(
rollNumber: myRollNumber, firstName: myFirstName, lastName: myLastName);
Provider.of<DataProvider>(context, listen: false)
.getMyStudentData(myStudentData);
}
Here is my DataProvider
class DataProvider extends ChangeNotifier {
StudentData myStudentData;
void getMyStudentData(StudentData studentData) {
myStudentData = studentData;
notifyListeners();
}
}
After that I have tried to fetch those information in my Screen
class StudentDashboard extends StatelessWidget {
#override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
body: Center(
child:
Text(Provider.of<DataProvider>(context).myStudentData.rollNumber),
),
),
);
}
}
Then the error be like
======== Exception caught by widgets library =======================================================
The following NoSuchMethodError was thrown building StudentDashboard(dirty, dependencies: [_InheritedProviderScope<DataProvider>]):
The getter 'rollNumber' was called on null.
Receiver: null
Tried calling: rollNumber
The relevant error-causing widget was:
StudentDashboard file:///D:/Other/App/Flutter/my_ecampus/lib/views/screens/auth_screens/login_screen.dart:62:53
When the exception was thrown, this was the stack:
#0 Object.noSuchMethod (dart:core-patch/object_patch.dart:51:5)
#1 StudentDashboard.build (package:my_ecampus/views/screens/main_screens/student_screens/student_dashboard.dart:38:69)
#2 StatelessElement.build (package:flutter/src/widgets/framework.dart:4701:28)
#3 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:4627:15)
#4 Element.rebuild (package:flutter/src/widgets/framework.dart:4343:5)
...
====================================================================================================
E/flutter ( 7682): [ERROR:flutter/lib/ui/ui_dart_state.cc(177)] Unhandled Exception: Looking up a deactivated widget's ancestor is unsafe.
E/flutter ( 7682): At this point the state of the widget's element tree is no longer stable.
E/flutter ( 7682): To safely refer to a widget's ancestor in its dispose() method, save a reference to the ancestor by calling dependOnInheritedWidgetOfExactType() in the widget's didChangeDependencies() method.
E/flutter ( 7682): #0 Element._debugCheckStateIsActiveForAncestorLookup.<anonymous closure> (package:flutter/src/widgets/framework.dart:3906:9)
E/flutter ( 7682): #1 Element._debugCheckStateIsActiveForAncestorLookup (package:flutter/src/widgets/framework.dart:3920:6)
E/flutter ( 7682): #2 Element.getElementForInheritedWidgetOfExactType (package:flutter/src/widgets/framework.dart:3986:12)
E/flutter ( 7682): #3 Provider._inheritedElementOf (package:provider/src/provider.dart:324:34)
E/flutter ( 7682): #4 Provider.of (package:provider/src/provider.dart:281:30)
E/flutter ( 7682): #5 getStudentData (package:my_ecampus/business_view/services/database/getData_database.dart:19:12)
E/flutter ( 7682): <asynchronous suspension>
E/flutter ( 7682): #6 LoginScreen._login (package:my_ecampus/views/screens/auth_screens/login_screen.dart:60:9)
E/flutter ( 7682): <asynchronous suspension>
E/flutter ( 7682): #7 LoginScreen.build.<anonymous closure>.<anonymous closure> (package:my_ecampus/views/screens/auth_screens/login_screen.dart:198:31)
E/flutter ( 7682): #8 _InkResponseState._handleTap (package:flutter/src/material/ink_well.dart:993:19)
E/flutter ( 7682): #9 _InkResponseState.build.<anonymous closure> (package:flutter/src/material/ink_well.dart:1111:38)
E/flutter ( 7682): #10 GestureRecognizer.invokeCallback (package:flutter/src/gestures/recognizer.dart:183:24)
E/flutter ( 7682): #11 TapGestureRecognizer.handleTapUp (package:flutter/src/gestures/tap.dart:598:11)
E/flutter ( 7682): #12 BaseTapGestureRecognizer._checkUp (package:flutter/src/gestures/tap.dart:287:5)
E/flutter ( 7682): #13 BaseTapGestureRecognizer.acceptGesture (package:flutter/src/gestures/tap.dart:259:7)
E/flutter ( 7682): #14 GestureArenaManager.sweep (package:flutter/src/gestures/arena.dart:157:27)
E/flutter ( 7682): #15 GestureBinding.handleEvent (package:flutter/src/gestures/binding.dart:362:20)
E/flutter ( 7682): #16 GestureBinding.dispatchEvent (package:flutter/src/gestures/binding.dart:338:22)
E/flutter ( 7682): #17 RendererBinding.dispatchEvent (package:flutter/src/rendering/binding.dart:267:11)
E/flutter ( 7682): #18 GestureBinding._handlePointerEvent (package:flutter/src/gestures/binding.dart:295:7)
E/flutter ( 7682): #19 GestureBinding._flushPointerEventQueue (package:flutter/src/gestures/binding.dart:240:7)
E/flutter ( 7682): #20 GestureBinding._handlePointerDataPacket (package:flutter/src/gestures/binding.dart:213:7)
E/flutter ( 7682): #21 _rootRunUnary (dart:async/zone.dart:1206:13)
E/flutter ( 7682): #22 _CustomZone.runUnary (dart:async/zone.dart:1100:19)
E/flutter ( 7682): #23 _CustomZone.runUnaryGuarded (dart:async/zone.dart:1005:7)
E/flutter ( 7682): #24 _invoke1 (dart:ui/hooks.dart:265:10)
E/flutter ( 7682): #25 _dispatchPointerDataPacket (dart:ui/hooks.dart:174:5)
E/flutter ( 7682):
This is my main class and i am using multiprovider here.
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
ChangeNotifierProvider<WidgetProvider>(
create: (context) => WidgetProvider()),
ChangeNotifierProvider<DataProvider>(
create: (context) => DataProvider()),
],
child: MaterialApp(
home: LoginScreen(),
),
);
}
}
This is where I call getStudentData function
void _login({String email, String password, BuildContext context}) async {
final loginResponse =
await loginDatabase(email: email, password: password, context: context);
if (loginResponse.isNotEmpty) {
final isStaff = email.contains(RegExp(r'.ce#srit.org$'));
if (isStaff == true) {
getStaffData(email, context);
Navigator.pushReplacement(
context, MaterialPageRoute(builder: (context) => StaffDashboard()));
} else {
getStudentData(email, context);//here is the getStudentData() function
Navigator.pushReplacement(context,
MaterialPageRoute(builder: (context) => StudentDashboard()));
}
} else {
print('Login Failed from loginDatabase(){}');
}
}
The HTTP request is sent to the provider listen: false because it can not listen. So that you can call the method. That's why I designed it from scratch for you. I hope you understand. It will probably work if you do it as I did.
import 'package:http/http.dart' as http;
Future<StudentData> _getStudentData(String currentEmail, BuildContext context)async {
final _url = 'https://aaa.000webhostapp.com/getStudentData.php';
final response = await http.post(_url, body: {'email': currentEmail});
var data = response.body;
if (data.isEmpty) return null;
final decodedData = jsonDecode(data);
final myRollNumber = decodedData['roll_number'];
final myFirstName = decodedData['first_name'];
final myLastName = decodedData['last_name'];
final myStudentData = StudentData(
rollNumber: myRollNumber, firstName: myFirstName, lastName: myLastName);
return MystudentData;
}
class DataProvider extends ChangeNotifier {
StudentData myStudentData;
void getMyStudentData(StudentData studentData) {
myStudentData = studentData;
notifyListeners();
}
}
Future getMyStudentDataAsync() async {
StudentData result = await _getStudentData();
getMyStudentData(result);
}
class StudentDashboard extends StatelessWidget {
#override
void initState() {
super.initState(); getRequest();
}
future getRequest()async{
Provider.of<DataProvider>(context, listen: false)
.getMyStudentDataAsync();
}
#override
Widget build(BuildContext context) {
DataProvider _dataProvider = Provider.of<DataProvider>(context);
return SafeArea(
child: Scaffold(
body: Center(
child:
Text( _dataProvider.myStudentData.rollNumber),
),
),
);
}
}

Flutter bloc_pattern returning NoSuchMethodError

I am building my first Flutter app, and foolishly enough, I started off with complex structures such as the MVVM, I don't know very much about it but have tried.
Here is my folder structure:
lib
---models
------videos.dart
---repos
------videos.dart
---viewmodels
------videos.dart
main.dart (I know)
In my main.dart file, I am using StreamBuilder to fetch the lisit of videos, here's the code:
class Home extends StatelessWidget {
VideosBloc videosBloc = BlocProvider.getBloc<VideosBloc>();
#override
Widget build(BuildContext context) {
return Column(
children: <Widget>[
Expanded(
flex: 1,
child: CustomAppBar(),
),
Expanded(
flex: 9,
child: Container(
color: Theme.of(context).primaryColorDark,
child: StreamBuilder(
stream: videosBloc.videosStream,
builder: (context, snapshot) {
List<Video> videos = snapshot != null ? snapshot.data : [];
return ListView(
children: videos.map((video) {
return VideoContainer(video);
}).toList(),
);
},
),
),
)
],
);
}
}
There there is this bloc file:
import 'dart:async';
import 'package:bloc_pattern/bloc_pattern.dart';
import 'package:peeke_vines/models/videos.dart';
import 'package:peeke_vines/repositories/videos.dart';
import 'package:rxdart/rxdart.dart';
class VideosBloc extends BlocBase {
VideosBloc();
//Stream that receives a number and changes the count;
var _videosController =
BehaviorSubject<List<Video>>.seeded(VideosRepo().getVideos());
//output
Stream<List<Video>> get videosStream => _videosController.stream;
//input
Sink<List<Video>> get videosSink => _videosController.sink;
//dispose will be called automatically by closing its streams
#override
void dispose() {
_videosController.close();
super.dispose();
}
}
And this bloc gets this data from a repository
import 'package:peeke_vines/models/videos.dart';
class VideosRepo {
VideosRepo();
List<Video> videos = VideoModel().fetchVideos();
List<Video> getVideos() {
return [];
}
}
And this repository currently just gets the data from a model, but later I'll implement a web service to fetch the data from:
import 'package:meta/meta.dart';
class VideoModel {
List<Video> fetchVideos() {
return [
Video(
video: 'assets/videos/main.wmv',
title: 'Peeke Vines Video Title',
date: '2 Days ago',
thumbnail: 'assets/thumbnails/1.jpg',
duration: 13.5),
Video(
video: 'assets/videos/main.wmv',
title: 'Peeke Vines Video Title',
date: '2 Days ago',
thumbnail: 'assets/thumbnails/2.jpg',
duration: 13.5),
Video(
video: 'assets/videos/main.wmv',
title: 'Peeke Vines Video Title',
date: '2 Days ago',
thumbnail: 'assets/thumbnails/1.jpg',
duration: 13.5)
];
}
}
class Video {
String video, title, date, thumbnail;
double duration;
Video(
{#required this.video,
#required this.title,
#required this.date,
#required this.thumbnail,
#required this.duration});
}
Now, whenever I import the bloc in the main.dart file using this line:
VideosBloc videosBloc = BlocProvider.getBloc<VideosBloc>();
I get this error:
I/flutter ( 4338): ══╡ EXCEPTION CAUGHT BY WIDGETS LIBRARY ╞═══════════════════════════════════════════════════════════
I/flutter ( 4338): The following NoSuchMethodError was thrown building MyApp(dirty):
I/flutter ( 4338): Class 'NoSuchMethodError' has no instance getter 'message'.
I/flutter ( 4338): Receiver: Instance of 'NoSuchMethodError'
I/flutter ( 4338): Tried calling: message
I/flutter ( 4338):
I/flutter ( 4338): When the exception was thrown, this was the stack:
I/flutter ( 4338): #0 Object.noSuchMethod (dart:core-patch/object_patch.dart:50:5)
I/flutter ( 4338): #1 BlocProvider.getBloc (package:bloc_pattern/src/bloc_provider.dart:47:12)
I/flutter ( 4338): #2 new Home (package:peeke_vines/main.dart:35:40)
I/flutter ( 4338): #3 MyApp.build (package:peeke_vines/main.dart:21:20)
I/flutter ( 4338): #4 StatelessElement.build (package:flutter/src/widgets/framework.dart:3974:28)
I/flutter ( 4338): #5 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:3924:15)
I/flutter ( 4338): #6 Element.rebuild (package:flutter/src/widgets/framework.dart:3721:5)
I/flutter ( 4338): #7 ComponentElement._firstBuild (package:flutter/src/widgets/framework.dart:3907:5)
I/flutter ( 4338): #8 ComponentElement.mount (package:flutter/src/widgets/framework.dart:3902:5)
I/flutter ( 4338): #9 Element.inflateWidget (package:flutter/src/widgets/framework.dart:3084:14)
I/flutter ( 4338): #10 Element.updateChild (package:flutter/src/widgets/framework.dart:2887:12)
I/flutter ( 4338): #11 RenderObjectToWidgetElement._rebuild (package:flutter/src/widgets/binding.dart:939:16)
I/flutter ( 4338): #12 RenderObjectToWidgetElement.mount (package:flutter/src/widgets/binding.dart:910:5)
I/flutter ( 4338): #13 RenderObjectToWidgetAdapter.attachToRenderTree.<anonymous closure> (package:flutter/src/widgets/binding.dart:856:17)
I/flutter ( 4338): #14 BuildOwner.buildScope (package:flutter/src/widgets/framework.dart:2320:19)
I/flutter ( 4338): #15 RenderObjectToWidgetAdapter.attachToRenderTree (package:flutter/src/widgets/binding.dart:855:13)
Can you tell me why this error is coming?
The issue is that you are trying to get the Bloc outside of the build method.
Try to get it at the start of the build method.

How to make a reusable PopupMenuButton in Flutter

I want to make a reusable PopupMenuButton in flutter, so i can call that widget and assign the PopupMenuItem dynamically.
Here is what i've done, but it throw a framework error. I am relatively new in flutter.
here is class that supposed to be reusable:
class PopupMenu {
PopupMenu({#required this.title, #required this.onTap});
final String title;
final VoidCallback onTap;
}
class PopupmMenuButtonBuilder {
setPopup(List<PopupMenu> popupItem) {
return PopupMenuButton<String>(
onSelected: (_) {
popupItem.forEach((item) => item.onTap);
},
itemBuilder: (BuildContext context) {
popupItem.forEach(
(item) {
return <PopupMenuItem<String>>[
PopupMenuItem<String>(
value: item.title,
child: Text(
item.title,
),
),
];
},
);
},
);
}
}
and then i call the widget like this:
child: PopupmMenuButtonBuilder().setPopup([
PopupMenu(title: 'Item 1', onTap: () => print('item 1 selected')),
PopupMenu(title: 'Item 2', onTap: () => print('item 2 selected')),
]),
It shows the 3 dot icon button, but when i tap the icon it throws this error:
I/flutter ( 8509): ══╡ EXCEPTION CAUGHT BY GESTURE ╞═══════════════════════════════════════════════════════════════════
I/flutter ( 8509): The following assertion was thrown while handling a gesture:
I/flutter ( 8509): 'package:flutter/src/material/popup_menu.dart': Failed assertion: line 723 pos 10: 'items != null &&
I/flutter ( 8509): items.isNotEmpty': is not true.
I/flutter ( 8509): Either the assertion indicates an error in the framework itself, or we should provide substantially
I/flutter ( 8509): more information in this error message to help you determine and fix the underlying cause.
I/flutter ( 8509): In either case, please report this assertion by filing a bug on GitHub:
I/flutter ( 8509): https://github.com/flutter/flutter/issues/new?template=BUG.md
I/flutter ( 8509): When the exception was thrown, this was the stack:
I/flutter ( 8509): #2 showMenu (package:flutter/src/material/popup_menu.dart:723:10)
I/flutter ( 8509): #3 _PopupMenuButtonState.showButtonMenu (package:flutter/src/material/popup_menu.dart:898:5)
I/flutter ( 8509): #4 _InkResponseState._handleTap (package:flutter/src/material/ink_well.dart:511:14)
I/flutter ( 8509): #5 _InkResponseState.build.<anonymous closure> (package:flutter/src/material/ink_well.dart:566:30)
I/flutter ( 8509): #6 GestureRecognizer.invokeCallback (package:flutter/src/gestures/recognizer.dart:166:24)
I/flutter ( 8509): #7 TapGestureRecognizer._checkUp (package:flutter/src/gestures/tap.dart:240:9)
I/flutter ( 8509): #8 TapGestureRecognizer.acceptGesture (package:flutter/src/gestures/tap.dart:211:7)
I/flutter ( 8509): #9 GestureArenaManager.sweep (package:flutter/src/gestures/arena.dart:156:27)
I/flutter ( 8509): #10 _WidgetsFlutterBinding&BindingBase&GestureBinding.handleEvent (package:flutter/src/gestures/binding.dart:225:20)
I/flutter ( 8509): #11 _WidgetsFlutterBinding&BindingBase&GestureBinding.dispatchEvent (package:flutter/src/gestures/binding.dart:199:22)
I/flutter ( 8509): #12 _WidgetsFlutterBinding&BindingBase&GestureBinding._handlePointerEvent (package:flutter/src/gestures/binding.dart:156:7)
I/flutter ( 8509): #13 _WidgetsFlutterBinding&BindingBase&GestureBinding._flushPointerEventQueue (package:flutter/src/gestures/binding.dart:102:7)I/flutter ( 8509): #14 _WidgetsFlutterBinding&BindingBase&GestureBinding._handlePointerDataPacket (package:flutter/src/gestures/binding.dart:86:7)I/flutter ( 8509): #18 _invoke1 (dart:ui/hooks.dart:233:10)
I/flutter ( 8509): #19 _dispatchPointerDataPacket (dart:ui/hooks.dart:154:5)
I/flutter ( 8509): (elided 5 frames from class _AssertionError and package dart:async)
I/flutter ( 8509): Handler: onTap
I/flutter ( 8509): Recognizer:
I/flutter ( 8509): TapGestureRecognizer#8968f(debugOwner: GestureDetector, state: ready, won arena, finalPosition:
I/flutter ( 8509): Offset(339.0, 54.0), sent tap down)
I/flutter ( 8509): ════════════════════════════════════════════════════════════════════════════════════════════════════
Your itemBuilder needs to return a List. It doesn't actually return anything - note how the return is inside the forEach so it's just returning from the lambda. In general, forEach should sometimes be avoided. Also, the PopupMenuButtonBuilder class is redundant - it could be replaced with a static or top-level function.
One other thing that's unclear is why you want to call each onTap for every select. As you currently have it it's going to call every callback!
Try this:
class PopupMenu {
PopupMenu({#required this.title, #required this.onTap});
final String title;
final VoidCallback onTap;
static PopupMenuButton<String> createPopup(List<PopupMenu> popupItems) {
return PopupMenuButton<String>(
onSelected: (value) {
popupItems.firstWhere((e) => e.title == value).onTap();
},
itemBuilder: (context) => popupItems
.map((item) => PopupMenuItem<String>(
value: item.title,
child: Text(
item.title,
),
))
.toList(),
);
}
}
You can also create a changeable list to append to the Popup menu instead of manually creating it one by one in the child
child: PopupMenu(popUpList: _popUpList).createPopup()),
then in the Popup menu class
class PopupMenu {
const PopupMenu({this.popUpList});
final List<PopUpList> popUpList;
PopupMenuButton<String> createPopup() {
return PopupMenuButton<String>(
onSelected: (value) {
popUpList.firstWhere((e) => e.title == value).onTap();
},
itemBuilder: (context) => popUpList
.map((item) => PopupMenuItem<String>(
value: item.title,
child: Text(item.title),
)).toList(),
);
}
}
model
class PopUpList{
String title;
VoidCallback onTap;
PopUpList({this.onTap, this.title});
}
cheers!

From StatefulWidget navigate to StatelessWidget runs the last builder but also the first builder

Is there a way to prevent the build() from being called multiple times?
The next code creates two pages, each with one button, the button on FirstPage navigates to SecondPage, and the one on SecondsPage goes back to FirstPage.
The problem is that if you click the button on FirstPage it runs the SecondPage build() but it also runs the FirstPage build().
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'First page',
home: FirstPage(),
);
}
}
class FirstPage extends StatefulWidget {
#override
State<StatefulWidget> createState() {
return _FirstPageState();
}
}
class _FirstPageState extends State<FirstPage> {
#override
Widget build(BuildContext context) {
print('---------- In FirstPage build ----------');
return Scaffold(
appBar: AppBar(
title: Text('First page'),
),
body: Column(
children: [
Text('First page body'),
FlatButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => SecondPage()),
);
},
child: Text('Go to second page')
)
]
)
);
}
}
class SecondPage extends StatelessWidget {
#override
Widget build(BuildContext context) {
print('---------- In SecondPage build ----------');
return Scaffold(
appBar: AppBar(
title: Text('Second page'),
),
body: Column(
children: [
Text('Second page body'),
FlatButton(
onPressed: () {
Navigator.pop(context);
},
child: Text('Go back')
)
]
)
);
}
}
Console output:
Performing hot restart...
D/dalvikvm(23908): threadid=12: interp stack at 0x76040000
Restarted app in 3,849ms.
I/flutter (23908): ---------- In FirstPage build ----------
I/SurfaceTextureClient(23908): [STC::queueBuffer] (this:0x76b39810) fps:0.07, dur:14731.13, max:14731.13, min:14731.13
I/SurfaceTextureClient(23908): [STC::queueBuffer] this:0x76b39810, api:1, last queue time elapsed:14731.13
After click button:
V/Provider/Settings(23908): from db cache, name = sound_effects_enabled , value = 0
I/flutter (23908): ---------- In SecondPage build ----------
I/SurfaceTextureClient(23908): [STC::queueBuffer] (this:0x76b39810) fps:0.24, dur:4140.60, max:4140.60, min:4140.60
I/flutter (23908): ---------- In FirstPage build ----------
Thanks for your help.
EDIT:
I tryed using popAndPushNamed() on FirstPage it just runs the build on the SecondPage but if I try to go back with:
Navigator.popAndPushNamed(
context,
'/'
);
I get the error:
I/flutter ( 5005): ══╡ EXCEPTION CAUGHT BY GESTURE ╞═══════════════════════════════════════════════════════════════════
I/flutter ( 5005): The following assertion was thrown while handling a gesture:
I/flutter ( 5005): Cannot reuse a MaterialPageRoute<dynamic> after disposing it.
I/flutter ( 5005): 'package:flutter/src/widgets/routes.dart': Failed assertion: line 215 pos 12:
I/flutter ( 5005): '!_transitionCompleter.isCompleted'
I/flutter ( 5005): Either the assertion indicates an error in the framework itself, or we should provide substantially
I/flutter ( 5005): more information in this error message to help you determine and fix the underlying cause.
I/flutter ( 5005): In either case, please report this assertion by filing a bug on GitHub:
I/flutter ( 5005): https://github.com/flutter/flutter/issues/new
I/flutter ( 5005): When the exception was thrown, this was the stack:
I/flutter ( 5005): #2 TransitionRoute.didPopNext (package:flutter/src/widgets/routes.dart)
I/flutter ( 5005): #3 NavigatorState.pop (package:flutter/src/widgets/navigator.dart:1691:23)
I/flutter ( 5005): #4 NavigatorState.popAndPushNamed (package:flutter/src/widgets/navigator.dart:1399:5)
I/flutter ( 5005): #5 Navigator.popAndPushNamed (package:flutter/src/widgets/navigator.dart:731:34)
I/flutter ( 5005): #6 SecondPage.build.<anonymous closure> (file:///home/ncs/Documents/flutter/src/test1/lib/main.dart:83:25)
I/flutter ( 5005): #7 _InkResponseState._handleTap (package:flutter/src/material/ink_well.dart:494:14)
I/flutter ( 5005): #8 _InkResponseState.build.<anonymous closure> (package:flutter/src/material/ink_well.dart:549:30)
I/flutter ( 5005): #9 GestureRecognizer.invokeCallback (package:flutter/src/gestures/recognizer.dart:102:24)
I/flutter ( 5005): #10 TapGestureRecognizer._checkUp (package:flutter/src/gestures/tap.dart:161:9)
I/flutter ( 5005): #11 TapGestureRecognizer.handlePrimaryPointer (package:flutter/src/gestures/tap.dart:94:7)
I/flutter ( 5005): #12 PrimaryPointerGestureRecognizer.handleEvent (package:flutter/src/gestures/recognizer.dart:315:9)
I/flutter ( 5005): #13 PointerRouter._dispatch (package:flutter/src/gestures/pointer_router.dart:73:12)
I/flutter ( 5005): #14 PointerRouter.route (package:flutter/src/gestures/pointer_router.dart:101:11)
I/flutter ( 5005): #15 _WidgetsFlutterBinding&BindingBase&GestureBinding.handleEvent (package:flutter/src/gestures/binding.dart:143:19)
I/flutter ( 5005): #16 _WidgetsFlutterBinding&BindingBase&GestureBinding.dispatchEvent (package:flutter/src/gestures/binding.dart:121:22)
I/flutter ( 5005): #17 _WidgetsFlutterBinding&BindingBase&GestureBinding._handlePointerEvent (package:flutter/src/gestures/binding.dart:101:7)
I/flutter ( 5005): #18 _WidgetsFlutterBinding&BindingBase&GestureBinding._flushPointerEventQueue (package:flutter/src/gestures/binding.dart:64:7)
I/flutter ( 5005): #19 _WidgetsFlutterBinding&BindingBase&GestureBinding._handlePointerDataPacket (package:flutter/src/gestures/binding.dart:48:7)
I/flutter ( 5005): #20 _invoke1 (dart:ui/hooks.dart:134:13)
I/flutter ( 5005): #21 _dispatchPointerDataPacket (dart:ui/hooks.dart:91:5)
I/flutter ( 5005): (elided 2 frames from class _AssertionError)
I/flutter ( 5005): Handler: onTap
I/flutter ( 5005): Recognizer:
I/flutter ( 5005): TapGestureRecognizer#94626(debugOwner: GestureDetector, state: possible, won arena, finalPosition:
I/flutter ( 5005): Offset(65.3, 119.6), sent tap down)
I/flutter ( 5005): ════════════════════════════════════════════════════════════════════════════════════════════════════
The only way I'm able to remove the unwanted build is by having a condition like::
class _FirstPageState extends State<FirstPage> {
bool _canBuild = true;
#override
Widget build(BuildContext context) {
if (!_canBuild) {
return Container();
}
print('---------- In FirstPage build ----------');
...
onPressed: () {
_canBuild = false;
Navigator.push(
context,
MaterialPageRoute(builder: (context) => SecondPage()),
).then((_) {
_canBuild = true;
});
},
...
Is this a bad practice or can create any problems?
it is just because of widget is still in to the stack of routes. i think that flutter re-render widgets which are in the stack. if you pop FirstPage before routing to SecondPage the it will not display the message of first widget.
EDIT:
how you can use.
onPressed: () {
Navigator.pop(context);
Navigator.push(
context,
MaterialPageRoute(builder: (context) => SecondPage()),
);
},
Flutter always builds every "page" (widget) in the navigation stack after every push/pop. This is normal and intended behavior, so that every widget can update itself if needed.
Widget's build methods are supposed to be idempotent, whilst usually it only builds when you think it will, the framework might decide at any point to build a widget for any reason - you can't rely on any widget not building.
This is not actually a problem normally, Flutter compares all new widgets with all old widgets on a build, if the widgets are identical they will not be replaced or in any other way affected.
The short answer is thus "No", of course if you want to you can hack around this (e.g. by overriding the navigator), you definitely can.
You can view the code related to building the children of a widget (including navigator) here: https://github.com/flutter/flutter/blob/master/packages/flutter/lib/src/widgets/framework.dart
But ideally you just want to assume anything can build at any time -- as it can. Flutter makes no promises as to when it will (or wont) build your widgets, any code that is not written to take this into account is liable to break!
See this issue: https://github.com/flutter/flutter/issues/11655