Flutter: Routing to widgets from a Drawer - flutter

Is there an example on how to create a draw app & reuse widgets occupying the screen? Yes I know there is an example in the gallery demo app. However that's a bit cheeky because that sample doesn't actually replace, set or change route to a different widget.
I've tried a the new instance of a widget from the onTap event of the drawer item. I've tried having my Getting duplicate global key errors when creating new instances of those widgets when an item is selected from the drawer and the old/new route is pop/pushed
ScheduleHomeWidget scheduleWidget = new ScheduleHomeWidget(onSendFeedback: widget.onSendFeedback,);
SpeakerListWidget speakerWidget = new SpeakerListWidget();
var routes = <String, WidgetBuilder> {
ScheduleHomeWidget.routeName : (BuildContext context) => scheduleWidget,
SpeakerListWidget.routeName : (BuildContext context) => speakerWidget
};
and my MaterialApp:
return new MaterialApp(
title: kAppTitle,
routes: routes,
home: scheduleWidget,
);
and the code to launch the new route:
onTap: () {
if (routeName != null) {
Timeline.instantSync('Start Transition', arguments: <String, String>{
'from': '/',
'to': routeName
});
Navigator.pop(context);
Navigator.pushNamed(context, routeName);
}
}
}
I'm not specifying a key when instantiating either the ScheduleHomeWidget or the SpeakerListWidget instantiation. That said, the ScheduleHomeWidget uses an AnimatedList which does use a GlobalKey<AnimatedListState>
I'll see the following exception:
══╡ EXCEPTION CAUGHT BY WIDGETS LIBRARY ╞═══════════════════════════════════════════════════════════
The following assertion was thrown building Flexible(flex: 1):
Multiple widgets used the same GlobalKey.
The key [LabeledGlobalKey<AnimatedListState>#d63d7] was used by multiple widgets. The parents of
those widgets were different widgets that both had the following description:
Flexible(flex: 1)
A GlobalKey can only be specified on one widget at a time in the widget tree.
When the exception was thrown, this was the stack:
#0 GlobalKey._debugReserveFor.<anonymous closure> (package:flutter/src/widgets/framework.dart:238:9)
#2 GlobalKey._debugReserveFor (package:flutter/src/widgets/framework.dart:219:12)
#3 Element.updateChild.<anonymous closure> (package:flutter/src/widgets/framework.dart:2524:13)
#5 Element.updateChild (package:flutter/src/widgets/framework.dart:2521:12)
#6 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:3403:16)
#7 Element.rebuild (package:flutter/src/widgets/framework.dart:3292:5)
#8 ProxyElement.update (package:flutter/src/widgets/framework.dart:3639:5)
#9 Element.updateChild (package:flutter/src/widgets/framework.dart:2542:15)
#10 RenderObjectElement.updateChildren (package:flutter/src/widgets/framework.dart:4066:32)
#11 MultiChildRenderObjectElement.update (package:flutter/src/widgets/framework.dart:4448:17)
#12 Element.updateChild (package:flutter/src/widgets/framework.dart:2542:15)
#13 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:3403:16)
#14 Element.rebuild (package:flutter/src/widgets/framework.dart:3292:5)
#15 ProxyElement.update (package:flutter/src/widgets/framework.dart:3639:5)
#16 Element.updateChild (package:flutter/src/widgets/framework.dart:2542:15)
#17 RenderObjectElement.updateChildren (package:flutter/src/widgets/framework.dart:4066:32)
#18 MultiChildRenderObjectElement.update (package:flutter/src/widgets/framework.dart:4448:17)
#19 Element.updateChild (package:flutter/src/widgets/framework.dart:2542:15)
#20 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:3403:16)
#21 Element.rebuild (package:flutter/src/widgets/framework.dart:3292:5)
#22 ProxyElement.update (package:flutter/src/widgets/framework.dart:3639:5)
#23 Element.updateChild (package:flutter/src/widgets/framework.dart:2542:15)
#24 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:3403:16)
#25 Element.rebuild (package:flutter/src/widgets/framework.dart:3292:5)
#26 StatefulElement.update (package:flutter/src/widgets/framework.dart:3528:5)
#27 Element.updateChild (package:flutter/src/widgets/framework.dart:2542:15)
#28 SingleChildRenderObjectElement.update (package:flutter/src/widgets/framework.dart:4340:14)
#29 Element.updateChild (package:flutter/src/widgets/framework.dart:2542:15)
#30 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:3403:16)
#31 Element.rebuild (package:flutter/src/widgets/framework.dart:3292:5)
#32 StatelessElement.update (package:flutter/src/widgets/framework.dart:3453:5)
#33 Element.updateChild (package:flutter/src/widgets/framework.dart:2542:15)
#34 SingleChildRenderObjectElement.update (package:flutter/src/widgets/framework.dart:4340:14)
#35 Element.updateChild (package:flutter/src/widgets/framework.dart:2542:15)
#36 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:3403:16)
#37 Element.rebuild (package:flutter/src/widgets/framework.dart:3292:5)
#38 StatefulElement.update (package:flutter/src/widgets/framework.dart:3528:5)
#39 Element.updateChild (package:flutter/src/widgets/framework.dart:2542:15)
#40 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:3403:16)
#41 Element.rebuild (package:flutter/src/widgets/framework.dart:3292:5)
#42 StatefulElement.update (package:flutter/src/widgets/framework.dart:3528:5)
#43 Element.updateChild (package:flutter/src/widgets/framework.dart:2542:15)
#44 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:3403:16)
#45 Element.rebuild (package:flutter/src/widgets/framework.dart:3292:5)
#46 ProxyElement.update (package:flutter/src/widgets/framework.dart:3639:5)
#47 Element.updateChild (package:flutter/src/widgets/framework.dart:2542:15)
#48 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:3403:16)
#49 Element.rebuild (package:flutter/src/widgets/framework.dart:3292:5)
#50 ProxyElement.update (package:flutter/src/widgets/framework.dart:3639:5)
#51 Element.updateChild (package:flutter/src/widgets/framework.dart:2542:15)
#52 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:3403:16)
#53 Element.rebuild (package:flutter/src/widgets/framework.dart:3292:5)
#54 StatefulElement.update (package:flutter/src/widgets/framework.dart:3528:5)
#55 Element.updateChild (package:flutter/src/widgets/framework.dart:2542:15)
#56 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:3403:16)
#57 Element.rebuild (package:flutter/src/widgets/framework.dart:3292:5)
#58 BuildOwner.buildScope (package:flutter/src/widgets/framework.dart:2142:33)
#59 BindingBase&SchedulerBinding&GestureBinding&ServicesBinding&RendererBinding&WidgetsBinding.drawFrame (package:flutter/src/widgets/binding.dart:503:20)
#60 BindingBase&SchedulerBinding&GestureBinding&ServicesBinding&RendererBinding._handlePersistentFrameCallback (package:flutter/src/rendering/binding.dart:189:5)
#61 BindingBase&SchedulerBinding._invokeFrameCallback (package:flutter/src/scheduler/binding.dart:688:15)
#62 BindingBase&SchedulerBinding.handleDrawFrame (package:flutter/src/scheduler/binding.dart:636:9)
#63 BindingBase&SchedulerBinding&GestureBinding&ServicesBinding&RendererBinding.scheduleWarmUpFrame.<anonymous closure> (package:flutter/src/rendering/binding.dart:275:20)
#65 _Timer._runTimers (dart:isolate-patch/timer_impl.dart:366)
#66 _Timer._handleMessage (dart:isolate-patch/timer_impl.dart:394)
#67 _RawReceivePortImpl._handleMessage (dart:isolate-patch/isolate_patch.dart:151)
(elided 3 frames from class _AssertionError and package dart:async-patch)
════════════════════════════════════════════════════════════════════════════════════════════════════
Reloaded 9 of 459 libraries in 2,143ms.
Another exception was thrown: 'package:flutter/src/widgets/framework.dart': Failed assertion: line 3727 pos 14: '_dependents.isEmpty': is not true.
Another exception was thrown: 'package:flutter/src/widgets/framework.dart': Failed assertion: line 1662 pos 12: '_elements.contains(element)': is not true.`
This error looks like Flutter is trying to add a widget to the tree by it's ID more than once. Not really trying to do that. Just looking to have two widgets that can take the main focus of the screen. Don't necessarily have to be created a new each time a user selects from the side bar.

What you can do here is add a Navigator on home. With this setup, you can manage the navigation with a Navigator key.
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
drawer: Drawer(
child: ListView(
children: _drawer(),
),
),
body: Navigator(
key: _navigatorKey, // use a Navigator key so the app won't be confused when managing routes
initialRoute: Nemo.pageA, // Set default page
...
),
);
}
The Navigator returns the Widget builder of the selected tab.
Navigator(
key: _navigatorKey, // use a Navigator key so the app won't be confused when managing routes
initialRoute: Nemo.pageA, // Set default page
onGenerateRoute: (RouteSettings settings) {
WidgetBuilder builder;
_currentPage = settings.name;
// Depending on the route name pushed
// set the Widget builder to be returned by the Navigator
// if pushed route is non-existent, return a 404 page
switch (settings.name) {
case Nemo.pageA:
builder = (BuildContext context) => const Center(child: Text('Page A'));
break;
case Nemo.pageB:
builder = (BuildContext context) => const Center(child: Text('Page B'));
break;
case Nemo.pageC:
builder = (BuildContext context) => const Center(child: Text('Page C'));
break;
default:
builder = (BuildContext context) => const Center(child: Text('404'));
}
return MaterialPageRoute(
builder: builder,
settings: settings,
);
},
),
On your drawer, you can push the route when the tab has been tapped. Check if the tab tapped is the current page displayed on screen so the same page won't be pushed by the Navigator again.
List<Widget> _drawer() {
return <Widget>[
ListTile(
title: const Text('Page A'),
onTap: () {
Navigator.pop(context);
if (_currentPage != Nemo.pageA) {
_navigatorKey.currentState!
.pushNamedAndRemoveUntil(Nemo.pageA, (Route route) => false);
}
},
),
...
];
}
Complete sample
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class Nemo {
static const String pageA = '/pageA';
static const String pageB = '/pageB';
static const String pageC = '/pageC';
}
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
final _navigatorKey = GlobalKey<NavigatorState>();
String? _currentPage;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
drawer: Drawer(
child: ListView(
children: _drawer(),
),
),
body: Navigator(
key:
_navigatorKey, // use a Navigator key so the app won't be confused when managing routes
initialRoute: Nemo.pageA, // Set default page
onGenerateRoute: (RouteSettings settings) {
WidgetBuilder builder;
_currentPage = settings.name;
// Depending on the route name pushed
// set the Widget builder to be returned by the Navigator
// if pushed route is non-existent, return a 404 page
switch (settings.name) {
case Nemo.pageA:
builder =
(BuildContext context) => const Center(child: Text('Page A'));
break;
case Nemo.pageB:
builder =
(BuildContext context) => const Center(child: Text('Page B'));
break;
case Nemo.pageC:
builder =
(BuildContext context) => const Center(child: Text('Page C'));
break;
default:
builder =
(BuildContext context) => const Center(child: Text('404'));
}
return MaterialPageRoute(
builder: builder,
settings: settings,
);
},
),
);
}
List<Widget> _drawer() {
return <Widget>[
ListTile(
title: const Text('Page A'),
onTap: () {
Navigator.pop(context);
if (_currentPage != Nemo.pageA) {
_navigatorKey.currentState!
.pushNamedAndRemoveUntil(Nemo.pageA, (Route route) => false);
}
},
),
ListTile(
title: const Text('Page B'),
onTap: () {
Navigator.pop(context);
if (_currentPage != Nemo.pageB) {
_navigatorKey.currentState!
.pushNamedAndRemoveUntil(Nemo.pageB, (Route route) => false);
}
},
),
ListTile(
title: const Text('Page C'),
onTap: () {
Navigator.pop(context);
if (_currentPage != Nemo.pageC) {
_navigatorKey.currentState!
.pushNamedAndRemoveUntil(Nemo.pageC, (Route route) => false);
}
},
),
];
}
}

Related

passing a function to from one stateful widget to another

I need to pass a function from one stateful widget to another.
Parent widget::
the function that I am passing is the following
void _setJobAddress(jobAddress) {
setState(() {
_jobAddress = jobAddress;
});
}
here is the button that opens the new view, this is how I am passing the function
TextButton(
onPressed: () => {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
SearchLocationScreen(_setJobAddress)))
}
this is a little bit of the child view where I need to call the function::
class SearchLocationScreen extends StatefulWidget {
const SearchLocationScreen({Key? key}) : super(key: key);
#override
State<SearchLocationScreen> createState() => _SearchLocationScreenState();
}
class _SearchLocationScreenState extends State<SearchLocationScreen> {
Function setJobAddess; // i tried adding this, but its not working
_SearchLocationScreenState(this.setJobAddess); // i tried adding this, but its not working
List<AutocompletePrediction> placePredictions = [];
How can I make this work so that when I click a button on the child widget, the parent function passed is called?
I tried the following:
class SearchLocationScreen extends StatefulWidget {
const SearchLocationScreen(
this.setJobAddess, {
Key? key,
}) : super(key: key);
final setJobAddess;
#override
State<SearchLocationScreen> createState() => _SearchLocationScreenState();
}
class _SearchLocationScreenState extends State<SearchLocationScreen> {
//Function setJobAddess;
//_SearchLocationScreenState(this.setJobAddess);
List<AutocompletePrediction> placePredictions = [];
#override
void initState() {
widget.setJobAddess();
}
... a bunch of code that is not of importance goes here
Padding(
padding: const EdgeInsets.all(defaultPadding),
child: ElevatedButton.icon(
onPressed: () async {
print('setting job location');
widget.setJobAddess('TEXT');
Navigator.pop(context);
},
icon: Icon(Icons.home),
label: const Text("Set job location"),
style: ElevatedButton.styleFrom(
backgroundColor: secondaryColor10LightTheme,
foregroundColor: textColorLightTheme,
elevation: 0,
fixedSize: const Size(double.infinity, 40),
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(10)),
),
),
),
),
I got the following error::
════════ Exception caught by widgets library ═══════════════════════════════════
The following NoSuchMethodError was thrown building Builder:
Closure call with mismatched arguments: function '_CreateUpdateJobViewState._setJobAddress'
Receiver: Closure: (dynamic) => void from Function '_setJobAddress#43263735':.
Tried calling: _CreateUpdateJobViewState._setJobAddress()
Found: _CreateUpdateJobViewState._setJobAddress(dynamic) => void
The relevant error-causing widget was
MaterialApp
lib/main.dart:20
When the exception was thrown, this was the stack
#0 Object.noSuchMethod (dart:core-patch/object_patch.dart:38:5)
#1 _objectNoSuchMethod (dart:core-patch/object_patch.dart:85:9)
#2 _SearchLocationScreenState.initState
package:ijob_clone_app/…/location/location_search_screen.dart:26
#3 StatefulElement._firstBuild
package:flutter/…/widgets/framework.dart:5015
#4 ComponentElement.mount
package:flutter/…/widgets/framework.dart:4853
... Normal element mounting (275 frames)
#279 Element.inflateWidget
package:flutter/…/widgets/framework.dart:3863
#280 MultiChildRenderObjectElement.inflateWidget
package:flutter/…/widgets/framework.dart:6435
#281 Element.updateChild
package:flutter/…/widgets/framework.dart:3592
#282 RenderObjectElement.updateChildren
package:flutter/…/widgets/framework.dart:5964
#283 MultiChildRenderObjectElement.update
package:flutter/…/widgets/framework.dart:6460
#284 Element.updateChild
package:flutter/…/widgets/framework.dart:3570
#285 ComponentElement.performRebuild
package:flutter/…/widgets/framework.dart:4904
#286 StatefulElement.performRebuild
package:flutter/…/widgets/framework.dart:5050
#287 Element.rebuild
package:flutter/…/widgets/framework.dart:4604
#288 StatefulElement.update
package:flutter/…/widgets/framework.dart:5082
#289 Element.updateChild
package:flutter/…/widgets/framework.dart:3570
#290 ComponentElement.performRebuild
package:flutter/…/widgets/framework.dart:4904
#291 Element.rebuild
package:flutter/…/widgets/framework.dart:4604
#292 ProxyElement.update
package:flutter/…/widgets/framework.dart:5228
#293 Element.updateChild
package:flutter/…/widgets/framework.dart:3570
#294 ComponentElement.performRebuild
package:flutter/…/widgets/framework.dart:4904
#295 Element.rebuild
package:flutter/…/widgets/framework.dart:4604
#296 ProxyElement.update
package:flutter/…/widgets/framework.dart:5228
#297 _InheritedNotifierElement.update
package:flutter/…/widgets/inherited_notifier.dart:107
#298 Element.updateChild
package:flutter/…/widgets/framework.dart:3570
#299 SingleChildRenderObjectElement.update
package:flutter/…/widgets/framework.dart:6307
#300 Element.updateChild
package:flutter/…/widgets/framework.dart:3570
#301 ComponentElement.performRebuild
package:flutter/…/widgets/framework.dart:4904
#302 StatefulElement.performRebuild
package:flutter/…/widgets/framework.dart:5050
#303 Element.rebuild
package:flutter/…/widgets/framework.dart:4604
#304 StatefulElement.update
package:flutter/…/widgets/framework.dart:5082
#305 Element.updateChild
package:flutter/…/widgets/framework.dart:3570
#306 SingleChildRenderObjectElement.update
package:flutter/…/widgets/framework.dart:6307
#307 Element.updateChild
package:flutter/…/widgets/framework.dart:3570
#308 SingleChildRenderObjectElement.update
package:flutter/…/widgets/framework.dart:6307
#309 Element.updateChild
package:flutter/…/widgets/framework.dart:3570
#310 ComponentElement.performRebuild
package:flutter/…/widgets/framework.dart:4904
#311 Element.rebuild
package:flutter/…/widgets/framework.dart:4604
#312 ProxyElement.update
package:flutter/…/widgets/framework.dart:5228
#313 Element.updateChild
package:flutter/…/widgets/framework.dart:3570
#314 ComponentElement.performRebuild
package:flutter/…/widgets/framework.dart:4904
#315 StatefulElement.performRebuild
package:flutter/…/widgets/framework.dart:5050
#316 Element.rebuild
package:flutter/…/widgets/framework.dart:4604
#317 BuildOwner.buildScope
package:flutter/…/widgets/framework.dart:2667
#318 WidgetsBinding.drawFrame
package:flutter/…/widgets/binding.dart:882
#319 RendererBinding._handlePersistentFrameCallback
package:flutter/…/rendering/binding.dart:378
#320 SchedulerBinding._invokeFrameCallback
package:flutter/…/scheduler/binding.dart:1175
#321 SchedulerBinding.handleDrawFrame
package:flutter/…/scheduler/binding.dart:1104
#322 SchedulerBinding._handleDrawFrame
package:flutter/…/scheduler/binding.dart:1015
#323 _invoke (dart:ui/hooks.dart:148:13)
#324 PlatformDispatcher._drawFrame (dart:ui/platform_dispatcher.dart:318:5)
#325 _drawFrame (dart:ui/hooks.dart:115:31)
════════════════════════════════════════════════════════════════════════════════
D/EGL_emulation(15747): app_time_stats: avg=25.04ms min=14.73ms max=497.29ms count=59
D/TrafficStats(15747): tagSocket(145) with statsTag=0xffffffff, statsUid=-1
D/TrafficStats(15747): tagSocket(121) with statsTag=0xffffffff, statsUid=-1
with this you can pass it and get it from the constructor.
class SearchLocationScreen extends StatefulWidget {
const SearchLocationScreen(this.setJobAddess,{Key? key, }) : super(key: key);
final setJobAddess;
#override
State<SearchLocationScreen> createState() => _SearchLocationScreenState();
}
class _SearchLocationScreenState extends State<SearchLocationScreen> {
/*now you can use call it with like this */
#override
void initState() {
widget.setJobAddess();
}
List<AutocompletePrediction> placePredictions = [];
// your other code
the StatefulWidget gives you the possibility to get properties from the constructor, and to access that property from the State class you need just to use widget.setJobAddess();
this is what worked for me
TextButton(
onPressed: () => {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => SearchLocationScreen(
setJobAddress: _setJobAddress)))
},
child: Text(_jobAddress),
),
child widget::
class SearchLocationScreen extends StatefulWidget {
final Function setJobAddress;
const SearchLocationScreen({Key? key, required this.setJobAddress})
: super(key: key);
#override
State<SearchLocationScreen> createState() => _SearchLocationScreenState();
}
class _SearchLocationScreenState extends State<SearchLocationScreen> {
List<AutocompletePrediction> placePredictions = [];
#override
...
Expanded(
child: ListView.builder(
itemCount: placePredictions.length,
itemBuilder: ((context, index) => LocationListTile(
press: () {
widget
.setJobAddress(placePredictions[index].description);
Navigator.pop(context);
},
location: placePredictions[index].description!,
))),
),

Flutter/dart:: Adding a new field to class causing an error

I added a new field to the class CloudNote, now I am getting an error!
I am getting the error when the app is trying to display a list.
Here is all my code without adding the field :: https://github.com/casas1010/flutter_firebase_vendor_management
I know its a simple issue, but I have tried to troubleshoot this for like an hour and have not made any progress
CloudNote class::
import 'package:cloud_firestore/cloud_firestore.dart';
import '/services/cloud/cloud_storage_constants.dart';
import 'package:flutter/foundation.dart';
/*
https://youtu.be/VPvVD8t02U8?t=87934
*/
#immutable
class CloudNote {
final String documentId;
final String jobCreatorId;
final String jobDescription;
final String jobState; // I added this
const CloudNote({
required this.documentId,
required this.jobCreatorId,
required this.jobDescription,
required this.jobState, // I added this
});
// acts as constructor
CloudNote.fromSnapshot(QueryDocumentSnapshot<Map<String, dynamic>> snapshot)
: documentId = snapshot.id,
jobCreatorId = snapshot.data()[jobCreatorIdColumn],
jobState = snapshot.data()[jobStateColumn], // I added this
jobDescription = snapshot.data()[jobDescriptionColumn] as String;
}
notes view ::
import 'package:flutter/material.dart';
import '/constants/routes.dart';
import '/enums/menu_action.dart';
import '/services/auth/auth_service.dart';
import '/services/cloud/cloud_note.dart';
import '/services/cloud/firebase_cloud_storage.dart';
import '/utilities/dialogs/logout_dialog.dart';
import '/views/notes/notes_list_view.dart';
class NotesView extends StatefulWidget {
const NotesView({Key? key}) : super(key: key);
#override
_NotesViewState createState() => _NotesViewState();
}
class _NotesViewState extends State<NotesView> {
late final FirebaseCloudStorage _notesService;
String get userId => AuthService.firebase().currentUser!.id;
#override
void initState() {
_notesService = FirebaseCloudStorage();
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Your jobs'),
actions: [
IconButton(
onPressed: () {
Navigator.of(context).pushNamed(createOrUpdateNoteRoute);
},
icon: const Icon(Icons.add),
),
PopupMenuButton<MenuAction>(
onSelected: (value) async {
switch (value) {
case MenuAction.logout:
final shouldLogout = await showLogOutDialog(context);
if (shouldLogout) {
await AuthService.firebase().logOut();
Navigator.of(context).pushNamedAndRemoveUntil(
loginRoute,
(_) => false,
);
}
}
},
itemBuilder: (context) {
return const [
PopupMenuItem<MenuAction>(
value: MenuAction.logout,
child: Text('Log out'),
),
];
},
)
],
),
body: StreamBuilder(
stream: _notesService.allNotes(jobCreatorId: userId),
builder: (context, snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.waiting:
case ConnectionState.active:
if (snapshot.hasData) {
final allNotes = snapshot.data as Iterable<CloudNote>;
return NotesListView(
notes: allNotes,
onDeleteNote: (note) async {
await _notesService.deleteNote(documentId: note.documentId);
},
onTap: (note) {
Navigator.of(context).pushNamed(
createOrUpdateNoteRoute,
arguments: note,
);
},
);
} else {
return const CircularProgressIndicator();
}
default:
return const CircularProgressIndicator();
}
},
),
);
}
}
Error
The following _TypeError was thrown building NotesListView(dirty):
type 'Null' is not a subtype of type 'String'
The relevant error-causing widget was
NotesListView
lib/…/notes/notes_view.dart:72
When the exception was thrown, this was the stack
#0 new CloudNote.fromSnapshot
package:ijob_clone_app/…/cloud/cloud_note.dart:26
#1 FirebaseCloudStorage.allNotes.<anonymous closure>.<anonymous closure>
package:ijob_clone_app/…/cloud/firebase_cloud_storage.dart:39
#2 MappedListIterable.elementAt (dart:_internal/iterable.dart:413:31)
#3 ListIterator.moveNext (dart:_internal/iterable.dart:342:26)
#4 WhereIterator.moveNext (dart:_internal/iterable.dart:438:22)
#5 Iterable.length (dart:core/iterable.dart:497:15)
#6 NotesListView.build
package:ijob_clone_app/…/notes/notes_list_view.dart:26
#7 StatelessElement.build
package:flutter/…/widgets/framework.dart:4949
#8 ComponentElement.performRebuild
package:flutter/…/widgets/framework.dart:4878
#9 Element.rebuild
package:flutter/…/widgets/framework.dart:4604
#10 ComponentElement._firstBuild
package:flutter/…/widgets/framework.dart:4859
#11 ComponentElement.mount
package:flutter/…/widgets/framework.dart:4853
#12 Element.inflateWidget
package:flutter/…/widgets/framework.dart:3863
#13 Element.updateChild
package:flutter/…/widgets/framework.dart:3586
#14 ComponentElement.performRebuild
package:flutter/…/widgets/framework.dart:4904
#15 StatefulElement.performRebuild
package:flutter/…/widgets/framework.dart:5050
#16 Element.rebuild
package:flutter/…/widgets/framework.dart:4604
#17 BuildOwner.buildScope
package:flutter/…/widgets/framework.dart:2667
#18 WidgetsBinding.drawFrame
package:flutter/…/widgets/binding.dart:882
#19 RendererBinding._handlePersistentFrameCallback
package:flutter/…/rendering/binding.dart:378
#20 SchedulerBinding._invokeFrameCallback
package:flutter/…/scheduler/binding.dart:1175
#21 SchedulerBinding.handleDrawFrame
package:flutter/…/scheduler/binding.dart:1104
#22 SchedulerBinding._handleDrawFrame
package:flutter/…/scheduler/binding.dart:1015
#23 _invoke (dart:ui/hooks.dart:148:13)
#24 PlatformDispatcher._drawFrame (dart:ui/platform_dispatcher.dart:318:5)
#25 _drawFrame (dart:ui/hooks.dart:115:31)
════════════════════════════════════════════════════════════════════════════════
NotesListView ::
import 'package:flutter/material.dart';
import '/services/cloud/cloud_note.dart';
import '/utilities/dialogs/delete_dialog.dart';
/*
source: https://www.youtube.com/watch?v=VPvVD8t02U8&t=59608s
class creation :: 22:02:54
*/
typedef NoteCallback = void Function(CloudNote note);
class NotesListView extends StatelessWidget {
final Iterable<CloudNote> notes; // list of notes
final NoteCallback onDeleteNote;
final NoteCallback onTap;
const NotesListView({
Key? key,
required this.notes,
required this.onDeleteNote,
required this.onTap,
}) : super(key: key);
#override
Widget build(BuildContext context) {
return ListView.builder(
itemCount: notes.length,
itemBuilder: (context, index) {
final note =
notes.elementAt(index); // current note whose data we are returning
return ListTile(
onTap: () {
onTap(note);
},
title: Text(
note.jobDescription,
maxLines: 1,
softWrap: true,
overflow: TextOverflow.ellipsis,
),
trailing: IconButton(
onPressed: () async {
final shouldDelete = await showDeleteDialog(context);
if (shouldDelete) {
onDeleteNote(note);
}
},
icon: const Icon(Icons.delete),
),
);
},
);
}
}
This happens if you forget to update your Firestore entries. Atleast one of your CloudNote entries in Firestore does not have the field jobState. That's why Firestore returns a Null value. But it tries to map to String which leads to an exception.
Make sure to rerun the project.
or
flutter clean
and then
flutter run

The following _CastError was thrown building FutureBuilder<WebViewController>

I made program using flutter webview
I have error with _FutureBuilderState<WebViewController
What I want to do make the reload button as FloatingActionButton
I guess this error is relevant with future async function, but I am not sure where to fix.
══╡ EXCEPTION CAUGHT BY WIDGETS LIBRARY ╞═══════════════════════════════════════════════════════════
The following _CastError was thrown building FutureBuilder<WebViewController>(dirty, state:
_FutureBuilderState<WebViewController>#54b59):
Null check operator used on a null value
The relevant error-causing widget was:
FutureBuilder<WebViewController>
FutureBuilder:file:///Users/whitebear/MyCode/httproot/guessdrawing_flutter/lib/main.dart:305:12
When the exception was thrown, this was the stack:
#0 NavigationControls.build.<anonymous closure> (package:flutterweb/main.dart:311:59)
#1 _FutureBuilderState.build (package:flutter/src/widgets/async.dart:782:55)
#2 StatefulElement.build (package:flutter/src/widgets/framework.dart:4782:27)
#3 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:4665:15)
#4 StatefulElement.performRebuild (package:flutter/src/widgets/framework.dart:4840:11)
#5 Element.rebuild (package:flutter/src/widgets/framework.dart:4355:5)
#6 ComponentElement._firstBuild (package:flutter/src/widgets/framework.dart:4643:5)
#7 StatefulElement._firstBuild (package:flutter/src/widgets/framework.dart:4831:11)
#8 ComponentElement.mount (package:flutter/src/widgets/framework.dart:4638:5)
... Normal element mounting (29 frames)
#37 Element.inflateWidget (package:flutter/src/widgets/framework.dart:3673:14)
#38 MultiChildRenderObjectElement.inflateWidget (package:flutter/src/widgets/framework.dart:6333:36)
#39 MultiChildRenderObjectElement.mount (package:flutter/src/widgets/framework.dart:6344:32)
... Normal element mounting (21 frames)
#60 Element.inflateWidget (package:flutter/src/widgets/framework.dart:3673:14)
#61 MultiChildRenderObjectElement.inflateWidget (package:flutter/src/widgets/framework.dart:6333:36)
#62 MultiChildRenderObjectElement.mount (package:flutter/src/widgets/framework.dart:6344:32)
... Normal element mounting (116 frames)
#178 Element.inflateWidget (package:flutter/src/widgets/framework.dart:3673:14)
#179 MultiChildRenderObjectElement.inflateWidget (package:flutter/src/widgets/framework.dart:6333:36)
#180 MultiChildRenderObjectElement.mount (package:flutter/src/widgets/framework.dart:6344:32)
... Normal element mounting (173 frames)
#353 Element.inflateWidget (package:flutter/src/widgets/framework.dart:3673:14)
#354 MultiChildRenderObjectElement.inflateWidget (package:flutter/src/widgets/framework.dart:6333:36)
#355 MultiChildRenderObjectElement.mount (package:flutter/src/widgets/framework.dart:6344:32)
... Normal element mounting (371 frames)
#726 Element.inflateWidget (package:flutter/src/widgets/framework.dart:3673:14)
#727 Element.updateChild (package:flutter/src/widgets/framework.dart:3425:18)
#728 RenderObjectToWidgetElement._rebuild (package:flutter/src/widgets/binding.dart:1198:16)
#729 RenderObjectToWidgetElement.mount (package:flutter/src/widgets/binding.dart:1167:5)
#730 RenderObjectToWidgetAdapter.attachToRenderTree.<anonymous closure> (package:flutter/src/widgets/binding.dart:1112:18)
#731 BuildOwner.buildScope (package:flutter/src/widgets/framework.dart:2573:19)
#732 RenderObjectToWidgetAdapter.attachToRenderTree (package:flutter/src/widgets/binding.dart:1111:13)
#733 WidgetsBinding.attachRootWidget (package:flutter/src/widgets/binding.dart:944:7)
#734 WidgetsBinding.scheduleAttachRootWidget.<anonymous closure> (package:flutter/src/widgets/binding.dart:924:7)
(elided 11 frames from class _RawReceivePortImpl, class _Timer, dart:async, and dart:async-patch)
my code is here.
class NavigationControls extends StatelessWidget {
const NavigationControls(this._webViewControllerFuture)
: assert(_webViewControllerFuture != null);
final Future<WebViewController> _webViewControllerFuture;
#override
Widget build(BuildContext context) {
return FutureBuilder<WebViewController>(
future: _webViewControllerFuture,
builder:
(BuildContext context, AsyncSnapshot<WebViewController> snapshot) {
final bool webViewReady =
snapshot.connectionState == ConnectionState.done;
final WebViewController controller = snapshot.data!;
//return IconButton(
return FloatingActionButton(
child: const Icon(Icons.replay),
onPressed: !webViewReady
? null
: () {
controller.reload();
},
);
},
);
}
NavigationControls is called here.
class WebViewExample extends StatefulWidget {
#override
_WebViewExampleState createState() => _WebViewExampleState();
}
class _WebViewExampleState extends State<WebViewExample> {
final Completer<WebViewController> _controller =
Completer<WebViewController>();
#override
void initState() {
super.initState();
if (Platform.isAndroid) WebView.platform = SurfaceAndroidWebView();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text(''),iew.
backgroundColor: Colors.white.withOpacity(0.0),
elevation: 0.0,
),
extendBodyBehindAppBar:true,
body: Builder(builder: (BuildContext context) {
return WebView(
javascriptMode: JavascriptMode.unrestricted,
onWebViewCreated: (WebViewController webViewController) {
_controller.complete(webViewController);
webViewController.loadUrl(
'https://example.com',
);
},
onProgress: (int progress) {
print("WebView is loading (progress : $progress%)");
},
javascriptChannels: <JavascriptChannel>{
_toasterJavascriptChannel(context),
},
navigationDelegate: (NavigationRequest request) {
if (request.url.startsWith('https://www.youtube.com/')) {
print('blocking navigation to $request}');
return NavigationDecision.prevent;
}
print('allowing navigation to $request');
return NavigationDecision.navigate;
},
onPageStarted: (String url) {
print('Page started loading: $url');
},
onPageFinished: (String url) {
print('Page finished loading: $url');
},
gestureNavigationEnabled: true,
);
}),
floatingActionButton:NavigationControls(_controller.future),
);
}
JavascriptChannel _toasterJavascriptChannel(BuildContext context) {
return JavascriptChannel(
name: 'Toaster',
onMessageReceived: (JavascriptMessage message) {
// ignore: deprecated_member_use
Scaffold.of(context).showSnackBar(
SnackBar(content: Text(message.message)),
);
});
}
}
The error is exactly at main.dart:305:12.
The only null check operator I can see in your code is:
final WebViewController controller = snapshot.data!;
So try changing your FAB to something like:
final bool webViewReady =
snapshot.connectionState == ConnectionState.done;
return FloatingActionButton(
child: const Icon(Icons.replay),
onPressed: !webViewReady
? null
: () {
snapshot.data!.reload();
},
This way you are not casting the type as non null before its used

This error appears when running the weather app I am using json and http library to fetch data but I don't know why this error appears

Error code
E/flutter (25456): [ERROR:flutter/lib/ui/ui_dart_state.cc(157)]
Unhandled Exception: NoSuchMethodError: The method '[]' was called on
null. E/flutter (25456): Receiver: null E/flutter (25456): Tried
calling: E/flutter (25456): #0 Object.noSuchMethod
(dart:core-patch/object_patch.dart:53:5) E/flutter (25456): #1
_WeatherState.fetchLocationUpDate. (package:tast_project/screenes/screenweather.dart:31:27) E/flutter
(25456): #2 State.setState
(package:flutter/src/widgets/framework.dart:1148:30) E/flutter
(25456): #3 _WeatherState.fetchLocationUpDate
(package:tast_project/screenes/screenweather.dart:30:5) E/flutter
(25456): E/flutter (25456): #4
_WeatherState.initState (package:tast_project/screenes/screenweather.dart:42:5) E/flutter
(25456): #5 StatefulElement._firstBuild
(package:flutter/src/widgets/framework.dart:4355:58) E/flutter
(25456): #6 ComponentElement.mount
(package:flutter/src/widgets/framework.dart:4201:5) E/flutter (25456):
#7 Element.inflateWidget (package:flutter/src/widgets/framework.dart:3194:14) E/flutter
(25456): #8 Element.updateChild
(package:flutter/src/widgets/framework.dart:2988:12) E/flutter
(25456): #9 SingleChildRenderObjectElement.mount
(package:flutter/src/widgets/framework.dart:5445:14) E/flutter
(25456): #10 Element.inflateWidget
(package:flutter/src/widgets/framework.dart:3194:14) E/flutter
(25456): #11 Element.updateChild
(package:flutter/src/widgets/framework.dart:2988:12) E/flutter
(25456): #12 ComponentElement.performRebuild
(package:flutter/src/widgets/framework.dart:4243:16) E/flutter
(25456): #13 Element.rebuild
(package:flutter/src/widgets/framework.dart:3947:5) E/flutter (25456):
#14 ComponentElement._firstBuild (package:flutter/src/widgets/framework.dart:4206:5) E/flutter (25456):
#15 ComponentElement.mount (package:flutter/src/widgets/framework.dart:4201:5) E/flutter (25456):
#16 Element.inflateWidget (package:flutter/src/widgets/framework.dart:3194:14) E/flutter
(25456): #17 Element.updateChild
(package:flutter/src/widgets/framework.dart:2988:12) E/flutter
(25456): #18 SingleChildRenderObjectElement.mount
(package:flutter/src/widgets/framework.dart:5445:14) E/flutter
(25456): #19 Element.inflateWidget
(package:flutter/src/widgets/framework.dart:3194:14) E/flutter
(25456): #20 Element.updateChild
(package:flutter/src/widgets/framework.dart:2988:12) E/flutter
(25456): #21 SingleChildRenderObjectElement.mount
(package:flutter/src/widgets/framework.dart:5445:14) E/flutter
(25456): #22 Element.inflateWidget
(package:flutter/src/widgets/framework.dart:3194:14) E/flutter
(25456): #23 Element.updateChild
(package:flutter/src/widgets/framework.dart:2988:12) E/flutter
(25456): #24 ComponentElement.performRebuild
(package:flutter/src/widgets/framework.dart:4243:16) E/flutter
(25456): #25 Element.rebuild
(package:flutter/src/widgets/framework.dart:3947:5) E/flutter (25456):
#26 ComponentElement._firstBuild (package:flutter/src/widgets/framework.dart:4206:5) E/flutter (25456):
#27 StatefulElement._firstBuild (package:flutter/src/widgets/framework.dart:4381:11) E/flutter
(25456): #28 ComponentElement.mount
(package:flutter/src/widgets/framework.dart:4201:5) E/flutter (25456):
#29 Element.inflateWidget (package:flutter/src/widgets/framework.dart:3194:14) E/flutter
(25456): #30 Element.updateChild
(package:flutter/src/widgets/framework.dart:2988:12) E/flutter
(25456): #31 SingleChildRenderObjectElement.mount
(package:flutter/src/widgets/framework.dart:5445:14) E/flutter
(25456): #32 Element.inflateWidget
(package:flutter/src/widgets/framework.dart:3194:14) E/flutter
(25456): #33 Element.updateChild
(package:flutter/src/widgets/framework.dart:2988:12) E/flutter
(25456): #34 SingleChildRenderObjectElement.mount
(package:flutter/src/widgets/framework.dart:5445:14) E/flutter
(25456): #35 Element.inflateWidget
(package:flutter/src/widgets/framework.dart:3194:14) E/flutter
(25456): #36 Element.updateChild
(package:flutter/src/widgets/framework.dart:2988:12) E/flutter
(25456): #37 ComponentElement.performRebuild
(package:flutter/src/widgets/framework.dart:4243:16) E/flutter
(25456): #38 Element.rebuild
(package:flutter/src/widgets/framework.dart:3947:5) E/flutter (25456):
#39 ComponentElement._firstBuild (package:flutter/src/widgets/framework.dart:4206:5) E/flutter (25456):
#40 StatefulElement._firstBuild (package:flutter/src/widgets/framework.dart:4381:11) E/flutter
(25456): #41 ComponentElement.mount
(package:flutter/src/widgets/framework.dart:4201:5) E/flutter (25456):
#42 Element.inflateWidget (package:flutter/src/widgets/framework.dart:3194:14) E/flutter
(25456): #43 Element.updateChild
(package:flutter/src/widgets/framework.dart:2988:12) E/flutter
(25456): #44 ComponentElement.performRebuild
(package:flutter/src/widgets/framework.da
Flutter code weather
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'dart:async';
import 'package:http/http.dart' as http;
import 'dart:convert';
class Weather extends StatefulWidget {
#override
_WeatherState createState() => _WeatherState();
}
const ipkay = '14f4090b86e2548527113ccac56a5ae4';
class _WeatherState extends State<Weather> {
var temp = 0;
String city='';
//var id = 4099194;
String location ;
var textEditingController = TextEditingController();
fetchSearch(String input) async {
http.Response response = await http.get(
"http://api.openweathermap.org/data/2.5/weather?units=metric&appid=14f4090b86e2548527113ccac56a5ae4&q=" + input);
var result = json.decode(response.body);
setState(() {
location =result['name'];
});}
fetchLocationUpDate()async{
http.Response response = await http.get(
'http://api.openweathermap.org/data/2.5/weather?units=metric&appid=14f4090b86e2548527113ccac56a5ae4&q=$location');
var result = json.decode(response.body);
setState(() {
temp =result['main']['temp'];
city =result['name'];
});}
glo(String city){
fetchSearch(city);
fetchLocationUpDate();}
#override
void initState() {
super.initState();
fetchLocationUpDate();}
#override
Widget build(BuildContext context) {
Size size = MediaQuery.of(context).size;
return Scaffold(
backgroundColor: Colors.transparent,
body: SafeArea(
child: Container(
width: size.width,
height: size.height,
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage(
'assetes/img/night.jpg',
),
fit: BoxFit.fill)),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'${temp.toString()} C°',
style: TextStyle(color: Colors.white, fontSize: 30),
),
Text(
'$city',
style: TextStyle(color: Colors.white, fontSize: 30),
),
Card(
child: TextField(
onSubmitted: (String city) {
glo(city);
},
controller: textEditingController,
decoration: InputDecoration(
hintText: 'please entre city',
prefixIcon: Icon(Icons.search),
),
),
),
)
],
),
),
),
);
}
You can copy paste run full code below
Step 1: double temp = 0;
Step 2: glo need async and await
glo(String city) async {
await fetchSearch(city);
await fetchLocationUpDate();
}
Step 3: Check city exist or not
if (result["cod"] == 200) {
setState(() {
location = result['name'];
print("locaton $location");
});
}
working demo
full code
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'dart:async';
import 'package:http/http.dart' as http;
import 'dart:convert';
class Weather extends StatefulWidget {
#override
_WeatherState createState() => _WeatherState();
}
const ipkay = '14f4090b86e2548527113ccac56a5ae4';
class _WeatherState extends State<Weather> {
double temp = 0;
//var id = 4099194;
String location;
var textEditingController = TextEditingController();
fetchSearch(String input) async {
http.Response response = await http.get(
"http://api.openweathermap.org/data/2.5/weather?units=metric&appid=14f4090b86e2548527113ccac56a5ae4&q=" +
input);
var result = json.decode(response.body);
print("fetchSearch ${response.body}");
if (result["cod"] == 200) {
setState(() {
location = result['name'];
print("locaton $location");
});
}
}
fetchLocationUpDate() async {
print("location to string ${location.toString()}");
http.Response response = await http.get(
'http://api.openweathermap.org/data/2.5/weather?units=metric&appid=14f4090b86e2548527113ccac56a5ae4&q=' +
location.toString());
print("LocationUpdate ${response.body}");
var result = json.decode(response.body);
if (result["cod"] == 200) {
setState(() {
temp = result['main']['temp'];
});
}
}
glo(String city) async {
await fetchSearch(city);
await fetchLocationUpDate();
}
#override
void initState() {
super.initState();
//fetchLocationUpDate();
}
#override
Widget build(BuildContext context) {
Size size = MediaQuery.of(context).size;
return Scaffold(
backgroundColor: Colors.transparent,
body: SafeArea(
child: Container(
width: size.width,
height: size.height,
decoration: BoxDecoration(
image: DecorationImage(
image: NetworkImage(
'https://picsum.photos/250?image=9',
),
fit: BoxFit.fill)),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'${temp.toDouble()} C°',
style: TextStyle(color: Colors.white, fontSize: 30),
),
Card(
child: TextField(
onSubmitted: (String city) {
glo(city);
},
controller: textEditingController,
decoration: InputDecoration(
hintText: 'please entre city',
prefixIcon: Icon(Icons.search),
),
),
),
// RaisedButton(
// onPressed: () {
// glo();
// },
// )
],
),
),
),
);
}
}
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: Weather(),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
void _incrementCounter() {
setState(() {
_counter++;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.headline4,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}

How to correctly mock NavigatorObserver in Flutter with Mockito?

I have a test where I'm trying to observe the behaviour of the [Navigator] when the app navigates from contacts_footer.dart to create_and_edit_contact.dart (push) and back (pop). Using verify from the Mockito package, I can successfully verify that the push behaviour works however, verifying the pop behaviour fails. The function _navigateToBack works as expected and testing for widgets that appear only in the contacts_footer.dart is successful, but observing the pop behaviour fails.
contacts_footer.dart
class ContactsFooter extends StatelessWidget {
static const navigateToEditPage = Key('navigateEdit');
const ContactsFooter({
Key key,
}) : super(key: key);
#override
Widget build(BuildContext context) {
return BottomAppBar(
color: Color.fromRGBO(244, 244, 244, 1),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
IconButton(
icon: Icon(Icons.edit),
onPressed: () {
Provider.of<Contacts>(context).setEditMode(true);
Navigator.of(context).push(MaterialPageRoute(
builder: (context) => CreateAndEditContact()));
},
key: ContactsFooter.navigateToEditPage,
)
],
),
);
}
}
create_and_edit_contact.dart
class CreateAndEditContact extends StatefulWidget {
static const routeName = 'edit-contact';
static var editOrCreateDetails = Key('editOrCreate');
#override
_CreateAndEditContactState createState() => _CreateAndEditContactState();
}
class _CreateAndEditContactState extends State<CreateAndEditContact> {
...
Widget build(BuildContext context) {
_isEditMode = Provider.of<Contacts>(context).isEditMode;
...
return Scaffold (
..
RaisedButton(key: CreateAndEditContact.editOrCreateDetails,
onPressed: () {
....
if (_isEditMode) {
print(true);
Provider.of<Contacts>(context)
.updateContact(context,formData, _selectedContact.vUuid)
.then((data) {
Navigator.of(context).pop();
});
} else {
print(false);
Provider.of<Contacts>(context)
.createContact(context,formData)
.then((data) {
Navigator.of(context).pop();
}).catchError(
(error) => showDialog(
context: context,
builder: (context) => ErrorDialog(
error.toString(),
),
),
);
}
},
child: Text(
'Sumbit',
style: TextStyle(color: Colors.white),
),
color: Theme.of(context).accentColor,
),
)
}
}
test file .
group('EditPage navigation tests', () {
NavigatorObserver mockObserver;
setUp(() {
mockObserver = MockNavigatorObserver();
});
Future<Null> _buildMainPage(WidgetTester tester) async {
await tester.pumpWidget(MaterialApp(
home: Scaffold(
body: Builder(
builder: (context) => Center(
child: MultiProvider(
providers: [
ChangeNotifierProvider<Contacts>(create: (_) => Contacts()),
],
child: Builder(
builder: (_) => MaterialApp(home: ContactsFooter(),
),
);,
),
),
),
/// This mocked observer will now receive all navigation events
/// that happen in our app.
navigatorObservers: <NavigatorObserver>[mockObserver],
));
/// The tester.pumpWidget() call above just built our app widget
/// and triggered the pushObserver method on the mockObserver once.
}
Future<Null> _navigateToDetailsPage(WidgetTester tester) async {
/// Tap the button which should navigate to the edit details page.
/// By calling tester.pumpAndSettle(), we ensure that all animations
/// have completed before we continue further.
await tester.tap(find.byKey(ContactsFooter.navigateToEditPage));
await tester.pumpAndSettle();
}
Future<Null> _navigateToBack(WidgetTester tester) async {
await tester.tap(find.byKey(CreateAndEditContact.editOrCreateDetails));
int num = await tester.pumpAndSettle();
print(num);
}
testWidgets(
'when tapping "navigate to edit details" button, should navigate to details page',
(WidgetTester tester) async {
await _buildMainPage(tester);
//CreateAndEditContact widget not present on screen as push event is not triggered yet
expect(find.byType(CreateAndEditContact), findsNothing);
//Trigger push event
await _navigateToDetailsPage(tester);
// By tapping the button, we should've now navigated to the edit details
// page. The didPush() method should've been called...
final Route pushedRoute =
verify(mockObserver.didPush(captureAny, any)).captured.single;
print(pushedRoute);
// there should be a CreateAndEditContact page present in the widget tree...
var createAndEdit = find.byType(CreateAndEditContact);
expect(createAndEdit, findsOneWidget);
await _navigateToBack(tester);
verify(mockObserver.didPop(any, any));
expect(find.byType(CreateAndEditContact), findsNothing);
expect(find.byKey(ContactsFooter.navigateToEditPage), findsWidgets);
});
}
All the expect statements execute correctly. However verify(mockObserver.didPop(any, any)); results in an exception as if [NavigatorObserver] did not recognize the pop behaviour.
>(RouteSettings("/", null), animation: AnimationController#1a3c6(⏭ 1.000; paused; for MaterialPageRoute<dynamic>(/)))
5
══╡ EXCEPTION CAUGHT BY FLUTTER TEST FRAMEWORK ╞════════════════════════════════════════════════════
The following TestFailure object was thrown running a test:
No matching calls. All calls: MockNavigatorObserver.navigator,
MockNavigatorObserver._navigator==NavigatorState#36772(tickers: tracking 1 ticker), [VERIFIED]
MockNavigatorObserver.didPush(MaterialPageRoute<dynamic>(RouteSettings("/", null), animation:
AnimationController#1a3c6(⏭ 1.000; paused; for MaterialPageRoute<dynamic>(/))), null)
(If you called `verify(...).called(0);`, please instead use `verifyNever(...);`.)
When the exception was thrown, this was the stack:
#0 fail (package:test_api/src/frontend/expect.dart:153:30)
#1 _VerifyCall._checkWith (package:mockito/src/mock.dart:648:7)
#2 _makeVerify.<anonymous closure> (package:mockito/src/mock.dart:935:18)
#3 main.<anonymous closure>.<anonymous closure> (file:///Users/calvin.gonsalves/Projects/Flutter/Dec23-2019/cmic_mobile_field/test/main_widget_test.dart:316:13)
<asynchronous suspension>
#4 testWidgets.<anonymous closure>.<anonymous closure> (package:flutter_test/src/widget_tester.dart:124:25)
#5 TestWidgetsFlutterBinding._runTestBody (package:flutter_test/src/binding.dart:696:19)
<asynchronous suspension>
#8 TestWidgetsFlutterBinding._runTest (package:flutter_test/src/binding.dart:679:14)
#9 AutomatedTestWidgetsFlutterBinding.runTest.<anonymous closure> (package:flutter_test/src/binding.dart:1050:24)
#15 AutomatedTestWidgetsFlutterBinding.runTest (package:flutter_test/src/binding.dart:1047:15)
#16 testWidgets.<anonymous closure> (package:flutter_test/src/widget_tester.dart:121:22)
#17 Declarer.test.<anonymous closure>.<anonymous closure>.<anonymous closure> (package:test_api/src/backend/declarer.dart:171:27)
<asynchronous suspension>
#18 Invoker.waitForOutstandingCallbacks.<anonymous closure> (package:test_api/src/backend/invoker.dart:242:15)
#23 Invoker.waitForOutstandingCallbacks (package:test_api/src/backend/invoker.dart:239:5)
#24 Declarer.test.<anonymous closure>.<anonymous closure> (package:test_api/src/backend/declarer.dart:169:33)
#29 Declarer.test.<anonymous closure> (package:test_api/src/backend/declarer.dart:168:13)
#30 Invoker._onRun.<anonymous closure>.<anonymous closure>.<anonymous closure>.<anonymous closure> (package:test_api/src/backend/invoker.dart:392:25)
#44 _Timer._runTimers (dart:isolate-patch/timer_impl.dart:384:19)
#45 _Timer._handleMessage (dart:isolate-patch/timer_impl.dart:418:5)
#46 _RawReceivePortImpl._handleMessage (dart:isolate-patch/isolate_patch.dart:174:12)
(elided 28 frames from class _FakeAsync, package dart:async, package dart:async-patch, and package stack_trace)
The test description was:
when tapping "navigate to edit details" button, should navigate to details page
════════════════════════════════════════════════════════════════════════════════════════════════════
Test failed. See exception logs above.
The test description was: when tapping "navigate to edit details" button, should navigate to details page
✖ EditPage navigation tests when tapping "navigate to edit details" button, should navigate to details page
I referred https://iirokrankka.com/2018/08/22/writing-widget-tests-for-navigation-events/
The issue seems to be caused by Navigator lacking context - usually provided by MaterialApp in this case. As you've mentioned in the comments, moving navigatorObservers inside MaterialApp solves the issue. Another workaround is to use navigatorKey to directly manage Navigator without obtaining it first from a BuildContext. See this similar Stack Overflow post on how you can utilize navigatorKey.