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
Related
I'm trying to display a scaffold msg when user inputted a value greater than some other value...
It is working fine until I clear the input...
After clearing the input it throws an exception...
Here it is...
══╡ EXCEPTION CAUGHT BY WIDGETS ╞═══════════════════════════════════════════════════════════════════
The following FormatException was thrown while calling onChanged:
Invalid number (at character 1)
^
When the exception was thrown, this was the stack:
#0 int._handleFormatError (dart:core-patch/integers_patch.dart:129:7)
#1 int.parse (dart:core-patch/integers_patch.dart:55:14)
#2 _GeneralTabState.build.<anonymous closure>.<anonymous closure>
(package:shop_app_vendor/screens/add_products/general_tab.dart:247:25)
#3 new TextFormField.<anonymous closure>.onChangedHandler (package:flutter/src/material/text_form_field.dart:188:25)
#4 EditableTextState._formatAndSetValue (package:flutter/src/widgets/editable_text.dart:2630:27)
#5 EditableTextState.userUpdateTextEditingValue (package:flutter/src/widgets/editable_text.dart:2919:5)
#6 EditableTextState._replaceText (package:flutter/src/widgets/editable_text.dart:3144:5)
#7 CallbackAction.invoke (package:flutter/src/widgets/actions.dart:534:39)
#8 ActionDispatcher.invokeAction (package:flutter/src/widgets/actions.dart:573:21)
#9 Actions.invoke.<anonymous closure> (package:flutter/src/widgets/actions.dart:872:48)
#10 Actions._visitActionsAncestors (package:flutter/src/widgets/actions.dart:653:18)
#11 Actions.invoke (package:flutter/src/widgets/actions.dart:866:30)
#12 _DeleteTextAction.invoke (package:flutter/src/widgets/editable_text.dart:4042:20)
#13 _OverridableContextAction.invokeDefaultAction (package:flutter/src/widgets/actions.dart:1696:28)
#14 _OverridableActionMixin.invoke (package:flutter/src/widgets/actions.dart:1559:9)
#15 ActionDispatcher.invokeAction (package:flutter/src/widgets/actions.dart:571:21)
#16 ShortcutManager.handleKeypress (package:flutter/src/widgets/shortcuts.dart:755:38)
#17 _ShortcutsState._handleOnKey (package:flutter/src/widgets/shortcuts.dart:956:20)
#18 FocusManager._handleKeyMessage (package:flutter/src/widgets/focus_manager.dart:1687:32)
#19 KeyEventManager._dispatchKeyMessage (package:flutter/src/services/hardware_keyboard.dart:828:34)
#20 KeyEventManager.handleRawKeyMessage (package:flutter/src/services/hardware_keyboard.dart:875:15)
#21 BasicMessageChannel.setMessageHandler.<anonymous closure> (package:flutter/src/services/platform_channel.dart:77:49)
#22 BasicMessageChannel.setMessageHandler.<anonymous closure> (package:flutter/src/services/platform_channel.dart:76:47)
#23 _DefaultBinaryMessenger.setMessageHandler.<anonymous closure> (package:flutter/src/services/binding.dart:380:35)
#24 _DefaultBinaryMessenger.setMessageHandler.<anonymous closure> (package:flutter/src/services/binding.dart:377:46)
#25 _invoke2.<anonymous closure> (dart:ui/hooks.dart:190:15)
#29 _invoke2 (dart:ui/hooks.dart:189:10)
#30 _ChannelCallbackRecord.invoke (dart:ui/channel_buffers.dart:42:5)
#31 _Channel.push (dart:ui/channel_buffers.dart:132:31)
#32 ChannelBuffers.push (dart:ui/channel_buffers.dart:329:17)
#33 PlatformDispatcher._dispatchPlatformMessage (dart:ui/platform_dispatcher.dart:589:22)
#34 _dispatchPlatformMessage (dart:ui/hooks.dart:89:31)
(elided 3 frames from dart:async)
════════════════════════════════════════════════════════════════════════════════════════════════════
And here's my code...
scf.dart
class SCF {
scaffoldMsg({context, msg}) {
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
content: Text(msg),
backgroundColor: Colors.red,
action: SnackBarAction(
label: 'OK',
textColor: Colors.white,
onPressed: () {
ScaffoldMessenger.of(context).clearSnackBars();
},
),
));
}
}
general_tab.dart
class GeneralTab extends StatefulWidget {
const GeneralTab({Key? key}) : super(key: key);
#override
State<GeneralTab> createState() => _GeneralTabState();
}
class _GeneralTabState extends State<GeneralTab> {
final SCF _scf = SCF();
#override
Widget build(BuildContext context) {
return Consumer<ProductProvider>(
builder: (context, provider, child) {
return ListView(
padding: const EdgeInsets.all(15.0),
children: [
// Regular Price
FormFieldInput(
label: 'Regular Price',
inputType: TextInputType.number,
onChanged: (value) {
provider.getFormData(regularPrice: int.parse(value));
},
),
// Sales Price
FormFieldInput(
label: 'Sales Price',
inputType: TextInputType.number,
onChanged: (value) {
if (int.parse(value) > provider.productData!['regularPrice']) {
_scf.scaffoldMsg(
context: context,
msg: 'Sales price should be less than regular price',
);
}
setState(() {
provider.getFormData(salesPrice: int.parse(value));
});
},
),
],
);
},
);
}
}
product_provider.dart
class ProductProvider with ChangeNotifier {
Map<String, dynamic>? productData = {};
getFormData({
int? regularPrice,
int? salesPrice,
}) {
if (regularPrice != null) {
productData!['regularPrice'] = regularPrice;
}
if (salesPrice != null) {
productData!['salesPrice'] = salesPrice;
}
notifyListeners();
}
}
form_field.dart
class FormFieldInput extends StatelessWidget {
final String? label;
final void Function(String)? onChanged;
final TextInputType? inputType;
const FormFieldInput({
Key? key,
this.label,
this.onChanged,
}) : super(key: key);
#override
Widget build(BuildContext context) {
return TextFormField(
decoration: InputDecoration(
label: Text(label!),
),
onChanged: onChanged,
);
}
}
This is working fine and showing scaffold msg until I delete the content in the input...
So how can I fix this?
that's because when the the text is empty the app cast the empty to number so this error happened when you reach the compare , so you need to check if the text is not empty before you trigger the compare.
so in your case in the onChange function before trigger any logic ,make sure that the value in the text field is not null or empty.
I found a solution.
By adding if(value.isNotEmpty) to the on change error is gone...
Need to check whether value is empty or not.
That's how I solved my problem.
Thank you
general_tab.dart
class GeneralTab extends StatefulWidget {
const GeneralTab({Key? key}) : super(key: key);
#override
State<GeneralTab> createState() => _GeneralTabState();
}
class _GeneralTabState extends State<GeneralTab> {
final SCF _scf = SCF();
#override
Widget build(BuildContext context) {
return Consumer<ProductProvider>(
builder: (context, provider, child) {
return ListView(
padding: const EdgeInsets.all(15.0),
children: [
// Regular Price
FormFieldInput(
label: 'Regular Price',
inputType: TextInputType.number,
onChanged: (value) {
if (value.isNotEmpty) {
provider.getFormData(regularPrice: int.parse(value));
}
},
),
// Sales Price
FormFieldInput(
label: 'Sales Price',
inputType: TextInputType.number,
onChanged: (value) {
if (value.isNotEmpty) {
if (int.parse(value) > provider.productData!['regularPrice']) {
_scf.scaffoldMsg(
context: context,
msg: 'Sales price should be less than regular price',
);
}
}
setState(() {
provider.getFormData(salesPrice: int.parse(value));
});
},
),
],
);
},
);
}
}
I have tried everything I know But Not been able to solve the issue
RUN Output :
Launching lib\main.dart on moto g 40 fusion in debug mode...
Running Gradle task 'assembleDebug'...
√ Built build\app\outputs\flutter-apk\app-debug.apk.
D/FlutterLocationService(30742): Creating service.
D/FlutterLocationService(30742): Binding to location service.
Debug service listening on ws://127.0.0.1:51746/KGrWp8utBGI=/ws
Syncing files to device moto g 40 fusion...
E/SQLiteLog(30742): (1) no such table: placestore in "SELECT * FROM placestore"
I/AssistStructure(30742): Flattened final assist data: 396 bytes, containing 1 windows, 3 views
D/MediaScannerConnection(30742): Scanned /data/user/0/com.example.memory_place/cache/04c048c7-d58f-497c-b050-fb56e943fc1b615751689670497232.mp4 to null
D/ThumbnailPlugin(30742): original w:1080, h:1920 => 150, 267
E/flutter (30742): [ERROR:flutter/lib/ui/ui_dart_state.cc(209)] Unhandled Exception: FileSystemException: Cannot copy file to '/data/user/0/com.example.memory_place/app_flutter/�$��� 5�5f��O ���
======== Exception caught by gesture ===============================================================
The following LateError was thrown while handling a gesture:
LateInitializationError: Field '_CurrentThumbnail#643317077' has not been initialized.
When the exception was thrown, this was the stack:
#0 AddScreen._CurrentThumbnail (package:memory_place/Screens/add_places_screen.dart)
#1 AddScreen.build.SafePlace (package:memory_place/Screens/add_places_screen.dart:25:47)
#2 AddScreen.build. (package:memory_place/Screens/add_places_screen.dart:64:15)
#3 _InkResponseState._handleTap (package:flutter/src/material/ink_well.dart:989:21)
#4 GestureRecognizer.invokeCallback (package:flutter/src/gestures/recognizer.dart:198:24)
#5 TapGestureRecognizer.handleTapUp (package:flutter/src/gestures/tap.dart:608:11)
#6 BaseTapGestureRecognizer._checkUp (package:flutter/src/gestures/tap.dart:296:5)
#7 BaseTapGestureRecognizer.handlePrimaryPointer (package:flutter/src/gestures/tap.dart:230:7)
#8 PrimaryPointerGestureRecognizer.handleEvent (package:flutter/src/gestures/recognizer.dart:563:9)
#9 PointerRouter._dispatch (package:flutter/src/gestures/pointer_router.dart:94:12)
#10 PointerRouter._dispatchEventToRoutes. (package:flutter/src/gestures/pointer_router.dart:139:9)
#11 _LinkedHashMapMixin.forEach (dart:collection-patch/compact_hash.dart:539:8)
#12 PointerRouter._dispatchEventToRoutes (package:flutter/src/gestures/pointer_router.dart:137:18)
#13 PointerRouter.route (package:flutter/src/gestures/pointer_router.dart:123:7)
#14 GestureBinding.handleEvent (package:flutter/src/gestures/binding.dart:439:19)
#15 GestureBinding.dispatchEvent (package:flutter/src/gestures/binding.dart:419:22)
#16 RendererBinding.dispatchEvent (package:flutter/src/rendering/binding.dart:322:11)
#17 GestureBinding._handlePointerEventImmediately (package:flutter/src/gestures/binding.dart:374:7)
#18 GestureBinding.handlePointerEvent (package:flutter/src/gestures/binding.dart:338:5)
#19 GestureBinding._flushPointerEventQueue (package:flutter/src/gestures/binding.dart:296:7)
#20 GestureBinding._handlePointerDataPacket (package:flutter/src/gestures/binding.dart:279:7)
#24 _invoke1 (dart:ui/hooks.dart:170:10)
#25 PlatformDispatcher._dispatchPointerDataPacket (dart:ui/platform_dispatcher.dart:331:7)
#26 _dispatchPointerDataPacket (dart:ui/hooks.dart:94:31)
(elided 3 frames from dart:async)
Handler: "onTap"
Recognizer: TapGestureRecognizer#12cd2
debugOwner: GestureDetector
state: possible
won arena
finalPosition: Offset(239.2, 911.2)
finalLocalPosition: Offset(239.2, 11.2)
button: 1
sent tap down
DB HELPER FILE:
import 'package:sqflite/sqflite.dart' as sql;
import 'package:path/path.dart' as p;
class dbHelper{
static Future<sql.Database> DataBase() async{
return sql.openDatabase(p.join(await sql.getDatabasesPath(),'memory_place.db'),
onCreate: (db, version) async{
return await db.execute('CREATE TABLE placestore(id TEXT PRIMARY KEY, title TEXT, video TEXT, thumbnail TEXT)');
},version: 1);
}
static Future<void> insert(String Table,Map<String,Object> data) async{
final db = await dbHelper.DataBase();
db.insert(
Table,
data,
conflictAlgorithm: sql.ConflictAlgorithm.replace);
}
static Future<List<Map<String,dynamic>>> getData(String table) async{
final db = await dbHelper.DataBase();
return db.query(table);
}
}
IMAGE INPUT PAGE :
import 'dart:async';
import 'dart:io';
import 'dart:typed_data';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';
import 'package:path_provider/path_provider.dart' as syspath;
import 'package:path/path.dart' as p;
import 'package:video_thumbnail/video_thumbnail.dart';
class ImageInput extends StatefulWidget {
Function onSelected;
ImageInput({required this.onSelected});
#override
State<ImageInput> createState() => _ImageInputState();
}
class _ImageInputState extends State<ImageInput> {
XFile ? _referenceXFileVid;
File? _referenceVidFile;
Future<void> TakePic() async{
final _imagePicker = ImagePicker();
final NewFile = await _imagePicker.pickVideo(
source: ImageSource.camera,
maxDuration: Duration(minutes: 1));
setState(() {
_referenceVidFile = File(NewFile!.path);
_referenceXFileVid = NewFile;
});
}
Future<Uint8List?> VideoThumbNail(XFile Video) async{
final uint8list = await VideoThumbnail.thumbnailData(
video: Video.path,
imageFormat: ImageFormat.JPEG,
maxWidth: 150,
quality: 25,
);
return await uint8list;
}
#override
Widget build(BuildContext context) {
return Row(
children: [
Container(
height: 267,
width: 150,
alignment: Alignment.center,
decoration: BoxDecoration(
border: Border.all(width: 1,color: Colors.grey),
),
child: _referenceVidFile!= null ?
FutureBuilder<Uint8List?>(
future: VideoThumbNail(_referenceXFileVid!),
builder: (context,uint8) {
if(uint8.hasData){
final ImagePath = uint8.data;
final CurrentThumbImg = Image.memory(ImagePath!,
height: 267,
width: 150,
fit: BoxFit.cover,);
SaveVidAndThumb(_referenceVidFile!,File.fromRawPath(ImagePath));
return CurrentThumbImg;
}else{
return Center(child: Text('Saumya Made Mistake',textAlign: TextAlign.center,));
}
}) : Text('No Video Taken',textAlign: TextAlign.center,),
),
SizedBox(width: 10,),
Expanded(
child: FlatButton.icon(onPressed: (){
TakePic();
},
icon: Icon(Icons.camera),
label: Text('Take Video'),
textColor: Theme.of(context).primaryColor,),
),
],
);
}
void SaveVidAndThumb(File Video,File Thumbnail) async{
final appDir = await syspath.getApplicationDocumentsDirectory();
final VideoPath = p.basename(Video.path);
final ThumbPath = p.basename(Thumbnail.path);
final Savedvideo = await Video.copy('${appDir.path}/$VideoPath');
final SavedThumb = await Thumbnail.copy('${appDir.path}/$ThumbPath');
widget.onSelected(Savedvideo,SavedThumb);
}
}
ADD PLACES SCREEN :
import 'dart:io';
import 'package:memory_place/models/Video_format.dart';
import 'package:memory_place/providers/place_provider.dart';
import 'package:memory_place/widgets/Location_Input.dart';
import 'package:provider/provider.dart';
import 'package:flutter/material.dart';
import 'package:memory_place/widgets/Image_Input.dart';
class AddScreen extends StatelessWidget {
TextEditingController _TitleTextController = TextEditingController();
late File _CurrentVideo;
late File _CurrentThumbnail;
void CallCurrentImage(File CurrentVideo,File CurrentThumbnail){
if(CurrentVideo!=null){
_CurrentVideo = CurrentVideo;
_CurrentThumbnail = CurrentThumbnail;
}
}
#override
Widget build(BuildContext context) {
void SafePlace(){
if(_TitleTextController.text == null || _CurrentThumbnail == null){
throw Text('Either Title or Image is Missing');
}else {
Provider.of<PlaceProvider>(context, listen: false).AddPlace(
_TitleTextController.text,
_CurrentVideo,
_CurrentThumbnail);
Navigator.pop(context);
}
}
return Scaffold(
appBar: AppBar(
title: Text('Add Place Screen'),
),
body: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Expanded(
child:SingleChildScrollView(
child: Padding(
padding: EdgeInsets.all(10.0),
child: Column(
children: [
TextField(
decoration: InputDecoration(label: Text('Title...')),
controller: _TitleTextController,
),
SizedBox(height: 20,),
ImageInput(onSelected: CallCurrentImage),
SizedBox(height: 20,),
LocationInput(),
],
),
),
) ),
RaisedButton.icon(
onPressed: (){
SafePlace();
},
icon: Icon(Icons.add),
label: Text('Add Place'),
elevation: 0,
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
color: Theme.of(context).accentColor,
)
],
),
);
}
}
When you declare a variable as late , you are actually indicating that it won't be null.
In your code, you're declaring_CurrentThumbnail variable as late but not
instantiating it.
You can change your AddScreen widget to StatefulWidget and instantiate _CurrentThumbnail inside initState function.(or even call CallCurrentImage inside initState)
I try to do login page (do the transaction in some Future function) and show the error message by Snackbar.
Click login
Show loading
Future transaction done (back to original page), and then show the error message
Here is the flow I want to achieve (the last part failed):
I don't know how to show the snackBar correctly using the message from future.
showSnackBar need BuildContext but the context inside the signIn page seems no longer valid anymore after the message come back from Future.
I am now using package flutter_hooks and hooks_riverpod for the state management.
My State
class MyState{
MyState({this.data,this.isLoading});
final bool isLoading;
final String data;
MyState copyWith({data, isLoading}) => MyState(data: data, isLoading: isLoading);
}
State control and provider
Future<String> getData() fetch data and return error message
class MyStateNotifier extends StateNotifier<MyState> {
MyStateNotifier(MyState state) : super(state);
Future<String> getData() async {
state = state.copyWith(isLoading: true);
await Future.delayed(Duration(seconds: 3)); // simulate getting data
state = state.copyWith(isLoading: false, data: 'some data');
return 'error message';
}
}
final myStateProvider = StateNotifierProvider<MyStateNotifier>((ref) {
return MyStateNotifier(MyState(data: null, isLoading: false));
});
My widget
myState.isLoading: show loading page or sign in page
class WidgetA extends HookWidget {
const WidgetA({Key key}) : super(key: key);
#override
Widget build(BuildContext context) {
final myState = useProvider(myStateProvider.state);
return Center(
child: myState.isLoading ? CircularProgressIndicator() : SignInPage(),
);
}
}
class SignInPage extends HookWidget {
const SignInPage({Key key}) : super(key: key);
#override
Widget build(BuildContext context) {
return RaisedButton(
onPressed: () async {
context.read(myStateProvider).getData().then(
(message) {
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(message)));
},
);
},
child: Text('login'),
);
}
}
I use showSnackBar inside then after getData(), but it show the error message:
E/flutter ( 6869): [ERROR:flutter/lib/ui/ui_dart_state.cc(177)] Unhandled Exception: Looking up a
deactivated widget's ancestor is unsafe.
E/flutter ( 6869): At this point the state of the widget's element tree is no longer stable.
E/flutter ( 6869): 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 ( 6869): #0 Element._debugCheckStateIsActiveForAncestorLookup.<anonymous closure> (package:flutter/src/widgets/framework.dart:3938:9)
E/flutter ( 6869): #1 Element._debugCheckStateIsActiveForAncestorLookup (package:flutter/src/widgets/framework.dart:3952:6)
E/flutter ( 6869): #2 Element.findAncestorWidgetOfExactType (package:flutter/src/widgets/framework.dart:4044:12)
E/flutter ( 6869): #3 debugCheckHasScaffoldMessenger.<anonymous closure> (package:flutter/src/material/debug.dart:142:17)
E/flutter ( 6869): #4 debugCheckHasScaffoldMessenger (package:flutter/src/material/debug.dart:154:4)
E/flutter ( 6869): #5 ScaffoldMessenger.of (package:flutter/src/material/scaffold.dart:218:12)
E/flutter ( 6869): #6 SignInPage.build.<anonymous closure>.<anonymous closure> (package:flutter_app_test2/main.dart:171:35)
E/flutter ( 6869): #7 _rootRunUnary (dart:async/zone.dart:1198:47)
E/flutter ( 6869): #8 _CustomZone.runUnary (dart:async/zone.dart:1100:19)
E/flutter ( 6869): #9 _FutureListener.handleValue (dart:async/future_impl.dart:143:18)
E/flutter ( 6869): #10 Future._propagateToListeners.handleValueCallback (dart:async/future_impl.dart:696:45)
E/flutter ( 6869): #11 Future._propagateToListeners (dart:async/future_impl.dart:725:32)
E/flutter ( 6869): #12 Future._completeWithValue (dart:async/future_impl.dart:529:5)
E/flutter ( 6869): #13 _completeOnAsyncReturn (dart:async-patch/async_patch.dart:254:13)
E/flutter ( 6869): #14 MyStateNotifier.getData (package:flutter_app_test2/main.dart)
E/flutter ( 6869): <asynchronous suspension>
You can copy paste run full code below
Reason : Because SignInPage disappear after click login button
Quick fix is use ScaffoldMessenger and provide scaffoldMessengerKey then call scaffoldMessengerKey.currentState.showSnackBar(SnackBar(content: Text(message)));
code snippet
final GlobalKey<ScaffoldMessengerState> scaffoldMessengerKey =
GlobalKey<ScaffoldMessengerState>();
class Home extends StatelessWidget {
#override
Widget build(BuildContext context) {
return ScaffoldMessenger(
key: scaffoldMessengerKey,
child: Scaffold(
...
context.read(myStateProvider).getData().then(
(message) {
scaffoldMessengerKey.currentState
.showSnackBar(SnackBar(content: Text(message)));
},
);
working demo
full code
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:hooks_riverpod/all.dart';
class MyState {
MyState({this.data, this.isLoading});
final bool isLoading;
final String data;
MyState copyWith({data, isLoading}) =>
MyState(data: data, isLoading: isLoading);
}
class MyStateNotifier extends StateNotifier<MyState> {
MyStateNotifier(MyState state) : super(state);
Future<String> getData() async {
state = state.copyWith(isLoading: true);
await Future.delayed(Duration(seconds: 3)); // simulate getting data
state = state.copyWith(isLoading: false, data: 'some data');
return 'error message';
}
}
final myStateProvider = StateNotifierProvider<MyStateNotifier>((ref) {
return MyStateNotifier(MyState(data: null, isLoading: false));
});
void main() {
runApp(
const ProviderScope(child: MyApp()),
);
}
class MyApp extends StatelessWidget {
const MyApp({Key key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(home: Home());
}
}
final GlobalKey<ScaffoldMessengerState> scaffoldMessengerKey =
GlobalKey<ScaffoldMessengerState>();
class Home extends StatelessWidget {
#override
Widget build(BuildContext context) {
return ScaffoldMessenger(
key: scaffoldMessengerKey,
child: Scaffold(
appBar: AppBar(title: const Text('example')),
body: WidgetA(),
),
);
}
}
class WidgetA extends HookWidget {
const WidgetA({Key key}) : super(key: key);
#override
Widget build(BuildContext context) {
final myState = useProvider(myStateProvider.state);
return Center(
child: myState.isLoading ? CircularProgressIndicator() : SignInPage(),
);
}
}
class SignInPage extends HookWidget {
const SignInPage({Key key}) : super(key: key);
#override
Widget build(BuildContext context) {
return RaisedButton(
onPressed: () async {
context.read(myStateProvider).getData().then(
(message) {
scaffoldMessengerKey.currentState
.showSnackBar(SnackBar(content: Text(message)));
},
);
},
child: Text('login'),
);
}
}
You'll have to await you getData() request and return the message in a variable, then call the snackbar. You're trying to call the snackbar inside the future call. This can't be done on the UI.
final message = await context.read(myStateProvider).getData();
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(message)));
I am trying to take an image and send it to IBM Watson to classify it into one of 3 custom classifiers. Below is all my code.
import 'dart:io';
import 'package:camera/camera.dart';
import 'package:flutter/material.dart';
import 'package:flutter_ibm_watson/flutter_ibm_watson.dart';
import 'package:ibm_visual_recog_img_file/connection.dart';
import 'package:ourearth2020/screens/Community.dart';
import 'package:path/path.dart';
import 'dart:async';
import 'package:image_picker/image_picker.dart';
import 'package:flutter_speed_dial/flutter_speed_dial.dart';
import 'package:path_provider/path_provider.dart';
class VisualPage extends StatefulWidget {
#override
_VisualPageState createState() => _VisualPageState();
}
class _VisualPageState extends State<VisualPage> {
CameraController _controller;
List cameras;
String path;
var galleryImage;
CameraDescription cameraDescription;
Future initCamera() async {
cameras = await availableCameras();
var frontCamera = cameras.first;
_controller = CameraController(frontCamera, ResolutionPreset.high);
try {
await _controller.initialize();
} catch (e) {}
print('Controller Is Init:' + _controller.value.isInitialized.toString());
displayPreview();
}
bool displayPreview() {
if (_controller == null || !_controller.value.isInitialized) {
return false;
} else {
return true;
}
}
Future getImageFromGallery() async {
var image = await ImagePicker.pickImage(source: ImageSource.gallery);
setState(() {
galleryImage = image;
});
print('GALLERY IMAGE' + galleryImage.toString());
return galleryImage;
}
#override
void dispose() {
// Dispose of the controller when the widget is disposed.
_controller.dispose();
super.dispose();
}
#override
void initState() {
super.initState();
print('Running');
initCamera();
}
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
body: Stack(children: [
displayPreview()
? AspectRatio(
aspectRatio: MediaQuery.of(context).size.width /
MediaQuery.of(context).size.height,
child: CameraPreview(_controller),
)
: Container(
child: CircularProgressIndicator(
valueColor: AlwaysStoppedAnimation<Color>(Colors.yellow),
),
),
Positioned(
top: MediaQuery.of(context).size.height - 120,
child: GestureDetector(
onTap: () async {
await getImageFromGallery();
Navigator.push(context, MaterialPageRoute(builder: (context) =>
DisplayPicture(image: galleryImage)
));
},
child: Icon(
Icons.image,
color: Colors.white,
size: 60,
)),
),
Positioned(
top: MediaQuery.of(context).size.height - 120,
left: MediaQuery.of(context).size.width / 2.2,
child: GestureDetector(
behavior: HitTestBehavior.translucent,
child: Container(
child: Icon(
Icons.camera,
color: Colors.white,
size: 60,
)),
onTap: () async {
final path = (await getTemporaryDirectory()).path +
'${DateTime.now()}.png';
try {
await _controller.takePicture(path);
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
DisplayPicture(imagePath: path)));
} catch (e) {
print('EEEE' + e);
}
}))
]));
}
}
class DisplayPicture extends StatelessWidget {
String imagePath;
File image;
String _text;
// File file = File(imagePath)
DisplayPicture({this.imagePath, this.image});
visualImageClassifier(File image) async{
IamOptions options = await IamOptions(iamApiKey: "NRDjngCby2d-pSHOPyWQJxhuB6vOY2uOTCX6KV2BCfwB", url: "https://api.us-south.visual-recognition.watson.cloud.ibm.com/instances/ef286f4e-84c7-44e0-b63d-a6a49a142a30").build();
VisualRecognition visualRecognition = new VisualRecognition(iamOptions: options, language: Language.ENGLISH); // Language.ENGLISH is language response
ClassifiedImages classifiedImages = await visualRecognition.classifyImageFile(image.path);
print(classifiedImages.getImages()[0].getClassifiers()[0]
.getClasses()[0]
.className);
// print("${image.toString()}");
// print('ACCESS'+options.accessToken);
//print(options);
//print("${image.path}");
//print('CLASSIFICATION'+classifiedImages.customClasses.toString()); // StreamBuilder(
// stream: StreamMyClassifier(
// image,
// 'NRDjngCby2d-pSHOPyWQJxhuB6vOY2uOTCX6KV2BCfwB', 'CompostxLandfillxRecycle_2056123069'),
// builder: (context, snapshot) {
// if (snapshot.hasData) {
// _text = snapshot.data;
// print(_text);
// }
// else {
// print('NO DATA AVAILABLE');
// }
//
// }
// );
}
#override
Widget build(BuildContext context) {
return Scaffold(body:Stack(children:[Center(child:image==null?Image.file(File(imagePath)):Image.file(image)),Positioned(
top: MediaQuery.of(context).size.height/2,
child: FloatingActionButton(onPressed:() async{
await visualImageClassifier(image==null?File(imagePath):image);
},
child:Icon(Icons.arrow_right)),
)]));
}
}
The image is successfully displayed on my screen but once I send it through the visualRecognition.classifyImageFile(....); it gives me an error saying I can not use image because it only supports String. I converted it to String but it gives me the error below.
[ERROR:flutter/lib/ui/ui_dart_state.cc(166)] Unhandled Exception: FileSystemException: Cannot retrieve length of file, path = 'File: '/data/user/0/com.example.ourearth2020/cache2020-09-17 18:50:16.530957.png'' (OS Error: No such file or directory, errno = 2)
E/flutter (17606): #0 _File.length.<anonymous closure> (dart:io/file_impl.dart:366:9)
E/flutter (17606): #1 _rootRunUnary (dart:async/zone.dart:1198:47)
E/flutter (17606): #2 _CustomZone.runUnary (dart:async/zone.dart:1100:19)
E/flutter (17606): #3 _FutureListener.handleValue (dart:async/future_impl.dart:143:18)
E/flutter (17606): #4 Future._propagateToListeners.handleValueCallback (dart:async/future_impl.dart:696:45)
E/flutter (17606): #5 Future._propagateToListeners (dart:async/future_impl.dart:725:32)
E/flutter (17606): #6 Future._completeWithValue (dart:async/future_impl.dart:529:5)
E/flutter (17606): #7 Future._asyncCompleteWithValue.<anonymous closure> (dart:async/future_impl.dart:567:7)
E/flutter (17606): #8 _rootRun (dart:async/zone.dart:1190:13)
E/flutter (17606): #9 _CustomZone.run (dart:async/zone.dart:1093:19)
E/flutter (17606): #10 _CustomZone.runGuarded (dart:async/zone.dart:997:7)
E/flutter (17606): #11 _CustomZone.bindCallbackGuarded.<anonymous closure> (dart:async/zone.dart:1037:23)
E/flutter (17606): #12 _microtaskLoop (dart:async/schedule_microtask.dart:41:21)
E/flutter (17606): #13 _startMicrotaskLoop (dart:async/schedule_microtask.dart:50:5)
E/flutter (17606):
Some of the questions I have: Can I get the confidence score using this? The last time I tried using those statements to classify the image it used a general classifier(if I input an image of a skyscraper it told me skyscraper) so how can I have it classify with my 3 classifiers?
Btw I already set up the IBM Cloud and it is fully functional. The library that I found from pub.dev is here https://pub.dev/packages/flutter_ibm_watson
EDIT error code for getImages() method
[ERROR:flutter/lib/ui/ui_dart_state.cc(166)] Unhandled Exception: NoSuchMethodError: The method 'getImages' was called on null.
E/flutter (31403): Receiver: null
E/flutter (31403): Tried calling: getImages()
E/flutter (31403): #0 Object.noSuchMethod (dart:core-patch/object_patch.dart:51:5)
E/flutter (31403): #1 DisplayPicture.visualImageClassifier (package:ourearth2020/screens/VisualPage.dart:147:30)
E/flutter (31403): <asynchronous suspension>
E/flutter (31403): #2 DisplayPicture.build.<anonymous closure> (package:ourearth2020/screens/VisualPage.dart:177:15)
E/flutter (31403): #3 _InkResponseState._handleTap (package:flutter/src/material/ink_well.dart:992:19)
E/flutter (31403): #4 _InkResponseState.build.<anonymous closure> (package:flutter/src/material/ink_well.dart:1098:38)
E/flutter (31403): #5 GestureRecognizer.invokeCallback (package:flutter/src/gestures/recognizer.dart:184:24)
E/flutter (31403): #6 TapGestureRecognizer.handleTapUp (package:flutter/src/gestures/tap.dart:524:11)
E/flutter (31403): #7 BaseTapGestureRecognizer._checkUp (package:flutter/src/gestures/tap.dart:284:5)
E/flutter (31403): #8 BaseTapGestureRecognizer.handlePrimaryPointer (package:flutter/src/gestures/tap.dart:219:7)
E/flutter (31403): #9 PrimaryPointerGestureRecognizer.handleEvent (package:flutter/src/gestures/recognizer.dart:477:9)
E/flutter (31403): #10 PointerRouter._dispatch (package:flutter/src/gestures/pointer_router.dart:78:12)
E/flutter (31403): #11 PointerRouter._dispatchEventToRoutes.<anonymous closure> (package:flutter/src/gestures/pointer_router.dart:124:9)
E/flutter (31403): #12 _LinkedHashMapMixin.forEach (dart:collection-patch/compact_hash.dart:377:8)
E/flutter (31403): #13 PointerRouter._dispatchEventToRoutes (package:flutter/src/gestures/pointer_router.dart:122:18)
E/flutter (31403): #14 PointerRouter.route (package:flutter/src/gestures/pointer_router.dart:108:7)
E/flutter (31403): #15 GestureBinding.handleEvent (package:flutter/src/gestures/binding.dart:220:19)
E/flutter (31403): #16 GestureBinding.dispatchEvent (package:flutter/src/gestures/binding.dart:200:22)
E/flutter (31403): #17 GestureBinding._handlePointerEvent (package:flutter/src/gestures/binding.dart:158:7)
E/flutter (31403): #18 GestureBinding._flushPointerEventQueue (package:flutter/src/gestures/binding.dart:104:7)
E/flutter (31403): #19 GestureBinding._handlePointerDataPacket (package:flutter/src/gestures/binding.dart:88:7)
E/flutter (31403): #20 _rootRunUnary (dart:async/zone.dart:1206:13)
E/flutter (31403): #21 _CustomZone.runUnary (dart:async/zone.dart:1100:19)
E/flutter (31403): #22 _CustomZone.runUnaryGuarded (dart:async/zone.dart:1005:7)
E/flutter (31403): #23 _invoke1 (dart:ui/hooks.dart:283:10)
E/flutter (31403): #24 _dispatchPointerDataPacket (dart:ui/hooks.dart:192:5)
E/flutter (31403):
You can copy past run full code below
You can see confidence score in working demo below
please change image.toString() to image.path because image is File
from
ClassifiedImages classifiedImages = await visualRecognition.classifyImageFile(image.toString());
to
ClassifiedImages classifiedImages = await visualRecognition.classifyImageFile(image.path);
working demo
working demo 2 for CameraPreview
I/flutter (31132): living room
full code
import 'package:camera/camera.dart';
import 'package:flutter/material.dart';
import 'dart:io';
import 'package:flutter_ibm_watson/flutter_ibm_watson.dart';
import 'package:image_picker/image_picker.dart';
import 'package:path_provider/path_provider.dart';
class VisualPage extends StatefulWidget {
#override
_VisualPageState createState() => _VisualPageState();
}
class _VisualPageState extends State<VisualPage> {
CameraController _controller;
List cameras;
String path;
var galleryImage;
CameraDescription cameraDescription;
Future initCamera() async {
cameras = await availableCameras();
var frontCamera = cameras.first;
_controller = CameraController(frontCamera, ResolutionPreset.high);
try {
await _controller.initialize();
} catch (e) {}
print('Controller Is Init:' + _controller.value.isInitialized.toString());
displayPreview();
setState(() {});
}
bool displayPreview() {
if (_controller == null || !_controller.value.isInitialized) {
return false;
} else {
return true;
}
}
Future getImageFromGallery() async {
var image = await ImagePicker.pickImage(source: ImageSource.gallery);
setState(() {
galleryImage = image;
});
print('GALLERY IMAGE' + galleryImage.toString());
return galleryImage;
}
#override
void dispose() {
// Dispose of the controller when the widget is disposed.
_controller.dispose();
super.dispose();
}
#override
void initState() {
super.initState();
print('Running');
initCamera();
}
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
body: Stack(children: [
displayPreview()
? AspectRatio(
aspectRatio: MediaQuery.of(context).size.width /
MediaQuery.of(context).size.height,
child: CameraPreview(_controller),
)
: Container(
child: CircularProgressIndicator(
valueColor: AlwaysStoppedAnimation<Color>(Colors.yellow),
),
),
Positioned(
top: MediaQuery.of(context).size.height - 120,
child: GestureDetector(
onTap: () async {
await getImageFromGallery();
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
DisplayPicture(image: galleryImage)));
},
child: Icon(
Icons.image,
color: Colors.white,
size: 60,
)),
),
Positioned(
top: MediaQuery.of(context).size.height - 120,
left: MediaQuery.of(context).size.width / 2.2,
child: GestureDetector(
behavior: HitTestBehavior.translucent,
child: Container(
child: Icon(
Icons.camera,
color: Colors.white,
size: 60,
)),
onTap: () async {
final path = (await getTemporaryDirectory()).path +
'${DateTime.now()}.png';
try {
await _controller.takePicture(path);
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
DisplayPicture(imagePath: path)));
} catch (e) {
print('EEEE' + e);
}
}))
]));
}
}
class DisplayPicture extends StatelessWidget {
String imagePath;
File image;
String _text;
// File file = File(imagePath)
DisplayPicture({this.imagePath, this.image});
visualImageClassifier(File image) async {
IamOptions options = await IamOptions(
iamApiKey: "NRDjngCby2d-pSHOPyWQJxhuB6vOY2uOTCX6KV2BCfwB",
url:
"https://api.us-south.visual-recognition.watson.cloud.ibm.com/instances/ef286f4e-84c7-44e0-b63d-a6a49a142a30")
.build();
VisualRecognition visualRecognition = new VisualRecognition(
iamOptions: options,
language: Language.ENGLISH); // Language.ENGLISH is language response
ClassifiedImages classifiedImages =
await visualRecognition.classifyImageFile(image.path);
print(classifiedImages
.getImages()[0]
.getClassifiers()[0]
.getClasses()[0]
.className);
// print("${image.toString()}");
// print('ACCESS'+options.accessToken);
//print(options);
//print("${image.path}");
//print('CLASSIFICATION'+classifiedImages.customClasses.toString()); // StreamBuilder(
// stream: StreamMyClassifier(
// image,
// 'NRDjngCby2d-pSHOPyWQJxhuB6vOY2uOTCX6KV2BCfwB', 'CompostxLandfillxRecycle_2056123069'),
// builder: (context, snapshot) {
// if (snapshot.hasData) {
// _text = snapshot.data;
// print(_text);
// }
// else {
// print('NO DATA AVAILABLE');
// }
//
// }
// );
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Stack(children: [
Center(
child:
image == null ? Image.file(File(imagePath)) : Image.file(image)),
Positioned(
top: MediaQuery.of(context).size.height / 2,
child: FloatingActionButton(
onPressed: () async {
await visualImageClassifier(
image == null ? File(imagePath) : image);
},
child: Icon(Icons.arrow_right)),
)
]));
}
}
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: VisualPage(),
);
}
}
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);
}
},
),
];
}
}