Show snackbar message after widget rebuild - flutter

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)));

Related

Getting exception when trying to open a new page with Navigator

I have a push notification manager class that is singleton where I listen for notifications from Firebase FCM and I handle them based on the logic and open specific screens.
I init with BuildContext that PushtNotificatonManager class inside HomePage that iS called from main.dart.
class HomePage extends StatefulWidget {
const HomePage({Key? key}) : super(key: key);
#override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
#override
void initState() {
super.initState();
PushNotificationsManager().listen(context);
}
This is code from main.dart
Future<void> backgroundHandler(RemoteMessage message) async {
print('Handling a background message ${message.messageId}');
print('Content of message: ' + message.toString());
}
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
configureInjections();
await Firebase.initializeApp(
options: DefaultFirebaseOptions.currentPlatform,
);
FirebaseMessaging.onBackgroundMessage(backgroundHandler);
runApp(SayApp());
}
This is part of the code from PushNotificationsManager class
#injectable
class PushNotificationsManager {
static final PushNotificationsManager _instance = PushNotificationsManager._internal();
factory PushNotificationsManager() {
return _instance;
}
PushNotificationsManager._internal();
Future<void> listen(BuildContext context) async {
if (Platform.isIOS) {
NotificationSettings settings = await FirebaseMessaging.instance.requestPermission(
alert: true,
badge: true,
provisional: false,
sound: true,
);
if (settings.authorizationStatus == AuthorizationStatus.authorized) {
print('User granted permission');
await _handleNotifications(context);
}
} else {
await _handleNotifications(context);
}
}
Future<void> _handleNotifications(BuildContext context) async {
// gives the message on which user taps and it opened the app from terminated state
FirebaseMessaging.instance.getInitialMessage().then((message) {
if (message != null) {
openNotification(message, context);
}
});
// foreground
FirebaseMessaging.onMessage.listen((message) {
if (message.notification != null) {
_showInAppNotification(message);
}
});
// When the app is in background but opened and user taps on the notification
FirebaseMessaging.onMessageOpenedApp.listen((message) {
openNotification(message, context);
});
}
Inside showInAppNotifications I am trying to display notifications as pop up and when the user clicks on it, I try to open a specific screen.
void _displayInAppNotifications(RemoteMessage message) {
FlutterRingtonePlayer.playNotification();
showOverlayNotification((context) {
return GestureDetector(
onTap: () {
openNotification(message, context);
OverlaySupportEntry.of(context)!.dismiss();
},
child: SafeArea(
child: Card(
child: ListTile(
leading: message.data[NotificationType.NOTIFICATION_TYPE] ==
NotificationType.PRIVATE_MESSAGE_1_ON_1
? Avatar(text: message.notification!.title!)
: SizedBox.fromSize(
size: const Size(40, 40),
child: ClipOval(
child: Container(
child: SvgPicture.asset(SvgIcons.sayAppWaveLogo),
))),
title:
Text(message.notification!.title!, overflow: TextOverflow.ellipsis, maxLines: 1),
subtitle: Text(
message.notification!.body!,
overflow: TextOverflow.ellipsis,
maxLines: 2,
),
trailing: IconButton(
icon: Icon(Icons.close),
onPressed: () {
OverlaySupportEntry.of(context)!.dismiss();
}),
),
),
),
);
}, duration: Duration(seconds: 4), position: NotificationPosition.top);
}
I have this method navigateToChannelDetailsPage that is called from the above method and here I am getting an exception.
Future<void> navigateToChannelDetailsPage(ChannelEntity channel, BuildContext context) async {
var cubit = BlocProvider.of<HomeCubit>(context);
var isUserChannelMember = await cubit.isCurrentUserChannelMember(channel);
if (!isUserChannelMember) return;
var isUserMutedPublic = await cubit.getIsUserMutedPublic(channel);
if (isUserMutedPublic) channel.isUserMutedPublic = isUserMutedPublic;
return PageNavigator.navigate(
context, ChannelDetailsPage(channel: channel, isFromAdminNotification: true))
.then((_) {
FocusManager.instance.primaryFocus?.unfocus();
cubit.updateChannelMemberChatDetailsDataOnChannelClosed(channel);
});
}
This is the exception:
E/flutter (25412): [ERROR:flutter/runtime/dart_vm_initializer.cc(41)] Unhandled Exception: Looking up a deactivated widget's ancestor is unsafe.
E/flutter (25412): At this point the state of the widget's element tree is no longer stable.
E/flutter (25412): 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 (25412): #0 Element._debugCheckStateIsActiveForAncestorLookup.<anonymous closure> (package:flutter/src/widgets/framework.dart:4241:9)
E/flutter (25412): #1 Element._debugCheckStateIsActiveForAncestorLookup (package:flutter/src/widgets/framework.dart:4255:6)
E/flutter (25412): #2 Element.getElementForInheritedWidgetOfExactType (package:flutter/src/widgets/framework.dart:4286:12)
E/flutter (25412): #3 Provider._inheritedElementOf (package:provider/src/provider.dart:339:38)
E/flutter (25412): #4 Provider.of (package:provider/src/provider.dart:293:30)
E/flutter (25412): #5 BlocProvider.of (package:flutter_bloc/src/bloc_provider.dart:100:23)
E/flutter (25412): #6 PushNotificationsManager.navigateToChannelDetailsPage (package:say_app/presentation/notification/push_notifications_manager.dart:204:30)
E/flutter (25412): #7 PushNotificationsManager.openNotification (package:say_app/presentation/notification/push_notifications_manager.dart:180:9)
E/flutter (25412): <asynchronous suspension>
Any ideas?
You get that error because, by the time you wait for some things to finish the execution, you do something else on the app (go back one or few screens let's say) so your widget/screen gets disposed of and is no longer in the widget tree when you call PageNavigator.navigate(context, ...).
So to prevent that from happening, you can use the mounted flag to check if your StatefulWidget is still active, like:
if(mounted) PageNavigator.navigate(context, ...)
But in that case, the page will not get pushed if the widget is disposed of. So if you still want to push the new screen, no matter what happens in the meantime (even if that widget gets disposed of), you can have your global navigatorKey which you assign to the MaterialApp key property and use the context from navigatorKey while navigating, since it should always be usable (i.e. the MaterialApp should always be present in the widget tree). Solution example:
import 'package:flutter/material.dart';
final GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
#override
Widget build(BuildContext context) {
return MaterialApp(
navigatorKey: navigatorKey,
home: const HomePage(),
);
}
}
So now you navigate like:
PageNavigator.navigate(navigatorKey.currentContext!, ...) // your use case
Navigator.of(navigatorKey.currentContext!).push(...) // the usual way using global navigatorKey

Flutter: Geolocator gives wrong location

When I execute, (click on the "show Longitude and Latitude" button) I have two problems, a wrong position and an error:
**W/GooglePlayServicesUtil( 5181): com.example.geolocalisation_youtube requires the Google Play Store, but it is missing.
I/flutter ( 5181): 10681894.898369517
I/flutter ( 5181): long c.longitude
I/flutter ( 5181): 37.421998333333335
E/flutter ( 5181): [ERROR:flutter/runtime/dart_vm_initializer.cc(41)] Unhandled Exception: PlatformException(IO_ERROR, A network error occurred trying to lookup the supplied coordinates (latitude: 37.421998, longitude: -122.084000)., null, null)
E/flutter ( 5181): #0 StandardMethodCodec.decodeEnvelope (package:flutter/src/services/message_codecs.dart:653:7)
E/flutter ( 5181): #1 MethodChannel._invokeMethod (package:flutter/src/services/platform_channel.dart:296:18)
E/flutter ( 5181): <asynchronous suspension>
E/flutter ( 5181): #2 MethodChannelGeocoding.placemarkFromCoordinates (package:geocoding_platform_interface/src/implementations/method_channel_geocoding.dart:56:24)
E/flutter ( 5181): <asynchronous suspension>
E/flutter ( 5181): #3 _MyHomePageState.build.<anonymous closure> (package:geolocalisation_youtube/main.dart:84:47)
E/flutter ( 5181): <asynchronous suspension>
**
Here’s the code:
import 'package:flutter/material.dart';
import 'package:geolocator/geolocator.dart';
import 'package:awesome_dialog/awesome_dialog.dart';
import 'package:geocoding/geocoding.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.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 MyHomePage extends StatefulWidget {
const MyHomePage({super.key, required this.title});
final String title;
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
Future getPosition() async{
bool service= await Geolocator.isLocationServiceEnabled();
LocationPermission per= await Geolocator.checkPermission();
if (per==LocationPermission.denied){
per= await Geolocator.requestPermission();
if(per!=LocationPermission.denied){
}
}
print(service);
print("---------------");
print(per);
print("---------------");
if(!service ){
AwesomeDialog(
context: context,
title: "services",
body:
Text("service is enabled")
)..show();
}
}
Future <Position> getLatandLong() async{
return await Geolocator.getCurrentPosition().then((value) => value);
}
#override
void initState(){
getPosition();
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Column(
children: [
Container(
height: 500,
width:400 ,
),
ElevatedButton(
onPressed: () async{
var c = await getLatandLong();
var distance= await Geolocator.distanceBetween(c.latitude, c.longitude, 28.033886, 1.659626);
print(distance);
print("long c.longitude ");
print(c.latitude);
List<Placemark> placemarks = await placemarkFromCoordinates(c.latitude, c.longitude);
print(placemarks[0].administrativeArea);
},
child: Text(" show Longitude and Latitude"))
],
),
);
}
}
I got this error on the latest version of Android Studio.

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

Flutter Error: StackOverflowError was thrown building Consumer<LocationProvider>(dirty, dependencies: [_InheritedProviderScope<LocationProvider?>])

i'm trying to include live location tracking into my app using the following packages:
google_maps_flutter 2.1.2 --> https://pub.dev/packages/google_maps_flutter/install
location 4.3.0 --> https://pub.dev/packages/location
provider 6.0.2 --> https://pub.dev/packages/provider
I am receiving the following error:
════════ Exception caught by widgets library ═══════════════════════════════════
The following StackOverflowError was thrown building Consumer<LocationProvider>(dirty, dependencies: [_InheritedProviderScope<LocationProvider?>]):
Stack Overflow
The relevant error-causing widget was
Consumer<LocationProvider>
lib/google_map_page.dart:31
When the exception was thrown, this was the stack
#0 LocationProvider.locationPosition
package:map_test/provider/location_provider.dart:55
#1 LocationProvider.locationPosition
package:map_test/provider/location_provider.dart:55
#2 LocationProvider.locationPosition
package:map_test/provider/location_provider.dart:55
#3 LocationProvider.locationPosition
package:map_test/provider/location_provider.dart:55
#4 LocationProvider.locationPosition
package:map_test/provider/location_provider.dart:55
#5 LocationProvider.locationPosition
package:map_test/provider/location_provider.dart:55
#6 LocationProvider.locationPosition
package:map_test/provider/location_provider.dart:55
#7 LocationProvider.locationPosition
package:map_test/provider/location_provider.dart:55
#8 LocationProvider.locationPosition
package:map_test/provider/location_provider.dart:55
#9 LocationProvider.locationPosition
package:map_test/provider/location_provider.dart:55
#10 LocationProvider.locationPosition
package:map_test/provider/location_provider.dart:55
#11 LocationProvider.locationPosition
package:map_test/provider/location_provider.dart:55
#12 LocationProvider.locationPosition
package:map_test/provider/location_provider.dart:55
#13 LocationProvider.locationPosition
package:map_test/provider/location_provider.dart:55
#14 LocationProvider.locationPosition
package:map_test/provider/location_provider.dart:55
#15 LocationProvider.locationPosition
package:map_test/provider/location_provider.dart:55
#16 LocationProvider.locationPosition
package:map_test/provider/location_provider.dart:55
#17 LocationProvider.locationPosition
package:map_test/provider/location_provider.dart:55
#18 LocationProvider.locationPosition
package:map_test/provider/location_provider.dart:55
#19 LocationProvider.locationPosition
package:map_test/provider/location_provider.dart:55
#20 LocationProvider.locationPosition
package:map_test/provider/location_provider.dart:55
#21 LocationProvider.locationPosition
package:map_test/provider/location_provider.dart:55
#22 LocationProvider.locationPosition
package:map_test/provider/location_provider.dart:55
#23 LocationProvider.locationPosition
package:map_test/provider/location_provider.dart:55
#24 LocationProvider.locationPosition
package:map_test/provider/location_provider.dart:55
#25 LocationProvider.locationPosition
package:map_test/provider/location_provider.dart:55
#26 LocationProvider.locationPosition
package:map_test/provider/location_provider.dart:55
......
package:map_test/provider/location_provider.dart:55 -->
LatLng get locationPosition => locationPosition;
There is 3 classes:
main.dart
google_map_page.dart
location_provider.dart
1. main.dart
import 'package:flutter/material.dart';
import 'package:map_test/google_map_page.dart';
import 'package:map_test/provider/location_provider.dart';
import 'package:provider/provider.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
ChangeNotifierProvider(
create: (context) => LocationProvider(),
child: const GoogleMapPage(),
)
],
child: MaterialApp(
title: 'Flutter Demo',
home: const GoogleMapPage(),
),
);
}
}
2. google_map_page.dart
import 'package:flutter/material.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
import 'package:map_test/provider/location_provider.dart';
import 'package:provider/provider.dart';
class GoogleMapPage extends StatefulWidget {
const GoogleMapPage({Key? key}) : super(key: key);
#override
State<GoogleMapPage> createState() => _GoogleMapPageState();
}
class _GoogleMapPageState extends State<GoogleMapPage> {
#override
void initState() {
super.initState();
Provider.of<LocationProvider>(context, listen: false).initalization();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Map2'),
backgroundColor: Colors.red,
),
body: googleMapUI());
}
Widget googleMapUI() {
return Consumer<LocationProvider>(builder: (consumerContext, model, child) {
if (model.locationPosition != null) {
return Column(
children: [
Expanded(
child: GoogleMap(
mapType: MapType.normal,
initialCameraPosition:
CameraPosition(target: model.locationPosition, zoom: 18),
myLocationEnabled: true,
myLocationButtonEnabled: true,
onMapCreated: (GoogleMapController controller) {},
),
)
],
);
}
return Container(
child: Center(
child: CircularProgressIndicator(),
),
);
});
}
}
3.location_provider.dart
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
import 'package:location/location.dart';
class LocationProvider with ChangeNotifier {
// Widget loc() {
// throw '';
// }
bool locationServiceActive = true;
LocationProvider() {
Location location;
location = new Location();
}
Location get location => location;
initalization() async {
await getUserLocation();
}
getUserLocation() async {
bool _serviceEnabled;
PermissionStatus _permissionGranted;
LatLng locationPosition;
_serviceEnabled = await location.serviceEnabled();
if (!_serviceEnabled) {
_serviceEnabled = await location.requestService();
if (!_serviceEnabled) {
return;
}
}
_permissionGranted = await location.hasPermission();
if (_permissionGranted == PermissionStatus.denied) {
_permissionGranted = await location.requestPermission();
if (_permissionGranted != PermissionStatus.granted) {
return;
}
}
location.onLocationChanged.listen((LocationData currentLocation) {
locationPosition = LatLng(
currentLocation.latitude!,
currentLocation.longitude!,
);
print(locationPosition);
notifyListeners();
});
}
LatLng get locationPosition => locationPosition; // line 55
}
It's my first time coding in flutter, i faced a lot of problem because of the Null Safety already. it just seems like the problems are endless.
any recommendations will be much appreciated.
Thank you.
The StackOverflowError is caused by self reference
LatLng get locationPosition => locationPosition;
means locationPosition is itself.
Hence will suggest you to amend the internal parameter variable by having prefix _
LatLng get locationPosition => _locationPosition;
while your class params and constructor can be written like below
class LocationProvider with ChangeNotifier {
LatLng? _locationPosition;
final Location _location = new Location();
LocationProvider();
Location get location => _location;
LatLng? get locationPosition => _locationPosition;
// other parts
}
I am not sure but try to wrap consumer in build method
Like this:
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Map2'),
backgroundColor: Colors.red,
),
body: Consumer<LocationProvider>(builder: (consumerContext, model, child) {
return googleMapUI()});
}
And add needed parameters

Unable to call Material page route in state class with async delay

I am trying to display a splash screen for a few seconds then trying to navigate it to the next screen but I am getting this error. Probably because of accessing context in iniState methods but I am also using async so I don't think there's should be any issue.
E/flutter ( 6663): [ERROR:flutter/lib/ui/ui_dart_state.cc(166)] Unhandled Exception: NoSuchMethodError: The method 'findAncestorStateOfType' was called on null.
E/flutter ( 6663): Receiver: null
E/flutter ( 6663): Tried calling: findAncestorStateOfType<NavigatorState>()
E/flutter ( 6663): #0 Object.noSuchMethod (dart:core-patch/object_patch.dart:51:5)
E/flutter ( 6663): #1 Navigator.of
package:flutter/…/widgets/navigator.dart:2185
import 'package:awsomeNotes/appUtilities/dimensions.dart';
import 'package:awsomeNotes/views/phoneAuthPage/phoneAuthPage.dart';
import 'package:flutter/material.dart';
class SplashScreen extends StatefulWidget {
#override
_SplashScreenState createState() => _SplashScreenState();
}
class _SplashScreenState extends State<SplashScreen> {
nextPage() async {
await Future.delayed(Duration(seconds: 5));
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => PhoneAuthPage(),
),
);
}
#override
void initState() {
nextPage();
super.initState();
}
#override
Widget build(BuildContext context) {
Dimensions(context);
return Material(
child: Center(
child: Text(
"This is Splash Screen",
style: TextStyle(fontSize: Dimensions.boxHeight * 5),
),
),
);
}
}
I am navigating to the next page but with the error specified.