I'm new to flutter and I just learned how to use isolates in Dart. When I try to access shared preference via an isolate it throws the given below error. This error also appears when I try to access Firebase analytics and remote config. How can I resolve this issue and access SharedPreference, FirebaseRemote config, FirebaseFirestore inside an isolate?
[ERROR:flutter/runtime/dart_isolate.cc(882)] Unhandled exception:
E/flutter (23694): ServicesBinding.defaultBinaryMessenger was accessed before the binding was initialized.
E/flutter (23694): If you're running an application and need to access the binary messenger before `runApp()` has been called (for example, during plugin initialization), then you need to explicitly call the `WidgetsFlutterBinding.ensureInitialized()` first.
E/flutter (23694): If you're running a test, you can call the `TestWidgetsFlutterBinding.ensureInitialized()` as the first line in your test's `main()` method to initialize the binding.
E/flutter (23694): #0 defaultBinaryMessenger.<anonymous closure> (package:flutter/src/services/binary_messenger.dart:92:7)
E/flutter (23694): #1 defaultBinaryMessenger (package:flutter/src/services/binary_messenger.dart:105:4)
E/flutter (23694): #2 MethodChannel.binaryMessenger (package:flutter/src/services/platform_channel.dart:143:62)
E/flutter (23694): #3 MethodChannel._invokeMethod (package:flutter/src/services/platform_channel.dart:149:36)
E/flutter (23694): #4 MethodChannel.invokeMethod (package:flutter/src/services/platform_channel.dart:332:12)
E/flutter (23694): #5 MethodChannel.invokeMapMethod (package:flutter/src/services/platform_channel.dart:359:49)
E/flutter (23694): #6 MethodChannelSharedPreferencesStore.getAll (package:shared_preferences_platform_interface/method_channel_shared_preferences.dart:54:22)
E/flutter (23694): #7 SharedPreferences._getSharedPreferencesMap (package:shared_preferences/shared_preferences.dart:191:57)
E/flutter (23694): #8 SharedPreferences.getInstance (package:shared_preferences/shared_preferences.dart:58:19)
E/flutter (23694): #9 _wraperState.islt (package:spynett/main.dart:122:55)
E/flutter (23694): <asynchronous suspension>
E/flutter (23694): #10 _startIsolate.<anonymous closure> (dart:isolate-patch/isolate_patch.dart:304:17)
E/flutter (23694): #11 _RawReceivePortImpl._handleMessage (dart:isolate-patch/isolate_patch.dart:168:12)
void main() async{
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
await setupLocator();
runApp(MyApp());
}
#override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_){
startinitsetup();
});
}
Future startinitsetup() async{
debugPrint('Calling setup');
await _dynamiclink.handledynamiclink();
await _pushNotificationService.initilalise();
await _remoteConfigService.initialise();
ReceivePort reciveport = ReceivePort();
Isolate.spawn(islt, reciveport.sendPort);
SendPort childSendPort = await reciveport.first;
ReceivePort responceport = ReceivePort();
childSendPort.send(['message',responceport.sendPort]);
await responceport.first;
}
static Future<int> islt(SendPort mainSendPort) async{
ReceivePort childRecivePort = ReceivePort();
mainSendPort.send(childRecivePort.sendPort);
await for (var message in childRecivePort){
SendPort replyport = message[1];
SharedPreferences _pref = await SharedPreferences.getInstance();
replyport.send('done');
}
}
You can copy paste run full code below
To use SharedPreferences in Isolate, you can use package https://pub.dev/packages/flutter_isolate
You can change from
Isolate.spawn(islt, reciveport.sendPort);
to
FlutterIsolate.spawn(islt, reciveport.sendPort);
output of full test code
I/flutter (12689): Calling setup
...
I/flutter (12689): test test
I/flutter (12689): isolate when msg recived
I/flutter (12689): reply done
full test code
import 'dart:isolate';
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:flutter_isolate/flutter_isolate.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
//await Firebase.initializeApp();
//await setupLocator();
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) {
startinitsetup();
});
}
Future startinitsetup() async {
debugPrint('Calling setup');
ReceivePort reciveport = ReceivePort();
FlutterIsolate.spawn(islt, reciveport.sendPort);
SendPort childSendPort = await reciveport.first;
ReceivePort responceport = ReceivePort();
childSendPort.send(['message', responceport.sendPort]);
String reply = await responceport.first;
print("reply $reply");
}
static Future<int> islt(SendPort mainSendPort) async {
ReceivePort childRecivePort = ReceivePort();
mainSendPort.send(childRecivePort.sendPort);
await for (var message in childRecivePort) {
SendPort replyport = message[1];
SharedPreferences _pref = await SharedPreferences.getInstance();
await _pref.setString("yourKey", "test");
String testPref = _pref.get("yourKey");
print("test $testPref");
debugPrint('isolate when msg recived');
replyport.send('done');
}
}
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),
),
);
}
}
Related
I have posted an issue on flutterfire github and the response is not helping me at all. So I hope someone here will help me guide to fix the problem I'm facing.
Also I have posted another question here but it is with GetX plugin and the response on github said they won't exactly know the problem since I'm using GetX. If you want to see it here is the link to it FirebaseMessaging.instance.getInitialMessage() not working with Splash Screen
Anyway, I created a sample Flutter app without GetX. After I created the app, I faced an error where it says
Null check operator used on a null value
How we handle this kind of error?
Here is my main.dart
import 'package:firebase_auth/firebase_auth.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter/material.dart';
import 'package:learningapp/home_screen.dart';
import 'package:learningapp/login_screen.dart';
import 'package:learningapp/result_screen.dart';
import 'package:learningapp/splash_screen.dart';
import 'local_notification_service.dart';
//receive notif if app is on background even if app terminated
Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
// ignore: avoid_print
print('Handling a background message: ${message.messageId}');
}
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);
runApp(const MyApp());
}
class MyApp extends StatefulWidget {
const MyApp({Key? key}) : super(key: key);
#override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
#override
void initState() {
super.initState();
LocalNotificationService.initialize(context);
//open notif content from terminated state of the app
FirebaseMessaging.instance.getInitialMessage().then((message) async {
if (message != null) {
final redirectRoute = message.data['route'];
if (redirectRoute != null) {
print('TERMINATED');
print('redirectRoute $redirectRoute');
Navigator.of(context).pushReplacementNamed(redirectRoute);
}
}
});
//only works if app in foreground
FirebaseMessaging.onMessage.listen((message) {
LocalNotificationService.display(message);
});
//onclick notif system tray only works if app in background but not terminated
FirebaseMessaging.onMessageOpenedApp.listen((message) {
final redirectRoute = message.data['route'];
if (redirectRoute != null) {
print('BACKGROUND');
print('redirectRoute $redirectRoute');
Navigator.of(context).pushReplacementNamed(redirectRoute);
}
});
}
bool isAuth = false;
Future<bool> _checkIfLoggedIn() async {
final currentUser = FirebaseAuth.instance.currentUser;
print(currentUser);
if (currentUser != null) {
return isAuth = true;
}
return isAuth = false;
}
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return FutureBuilder(
future: Future.delayed(const Duration(seconds: 3)),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
print('isAuth : $isAuth');
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
initialRoute: isAuth ? 'home' : 'login',
routes: {
'home': (context) => const HomeScreen(),
'login': (context) => const LoginScreen(),
'result': (context) => const ResultScreen(),
},
);
}
return FutureBuilder(
future: _checkIfLoggedIn(),
builder: (context, snapshot) {
return const SplashScreen();
},
);
},
);
}
}
Here is my local notification service class
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter/material.dart';
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
class LocalNotificationService {
static final FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin =
FlutterLocalNotificationsPlugin();
static void initialize(BuildContext context) {
const InitializationSettings initializationSettings =
InitializationSettings(
android: AndroidInitializationSettings('#mipmap/ic_launcher'));
flutterLocalNotificationsPlugin.initialize(
initializationSettings,
onSelectNotification: ((redirectRoute) async {
if (redirectRoute != null) {
print('FOREGROUND');
print('redirectRoute $redirectRoute');
Navigator.of(context).pushReplacementNamed(redirectRoute);
}
}),
);
}
static void display(RemoteMessage remoteMessage) async {
try {
final id = DateTime.now().millisecondsSinceEpoch ~/ 1000;
const NotificationDetails notificationDetails = NotificationDetails(
android: AndroidNotificationDetails(
"learningapp",
"learningapp channel",
importance: Importance.max,
priority: Priority.high,
color: Color(0xFF42A5F5),
),
);
await flutterLocalNotificationsPlugin.show(
id,
remoteMessage.notification!.title,
remoteMessage.notification!.body,
notificationDetails,
payload: remoteMessage.data['route'],
);
} on Exception catch (e) {
// ignore: avoid_print
print(e);
}
}
}
Here is the sample log output
C:\Users\User\Desktop\learningapp>flutter run --release
Launching lib\main.dart on RNE L22 in release mode...
Running Gradle task 'assembleRelease'...
Note: C:\flutter\.pub-cache\hosted\pub.dartlang.org\firebase_messaging-11.2.8\android\src\main\java\io\flutter\plugins\firebase\messaging\JobIntentService.java uses or overrides a deprecated API.
Running Gradle task 'assembleRelease'...
Note: Recompile with -Xlint:deprecation for details.
Running Gradle task 'assembleRelease'...
61.6s
√ Built build\app\outputs\flutter-apk\app-release.apk
(7.8MB).
Installing build\app\outputs\flutter-apk\app.apk...
2,839ms
Flutter run key commands.
h List all available interactive commands.
c Clear the screen
q Quit (terminate the application on the device).
I/flutter ( 9987): User(displayName: , email: test#mail.com, emailVerified: false, isAnonymous: false, metadata: UserMetadata(creationTime: 2022-03-10 14:46:11.419, lastSignInTime: 2022-03-10 17:55:45.210), phoneNumber: , photoURL: null, providerData, [UserInfo(displayName: , email: test#mail.com, phoneNumber: , photoURL: null, providerId: password, uid: test#mail.com)], refreshToken: , tenantId: null, uid: RpEG5Y5qt7Y7aGvJ6Mf2FweAlf33)
W/FlutterJNI( 9987): FlutterJNI.loadLibrary called more than once
W/FlutterJNI( 9987): FlutterJNI.prefetchDefaultFontManager called more than once
W/FlutterJNI( 9987): FlutterJNI.init called more than once
I/flutter ( 9987): isAuth : true
I/flutter ( 9987): FOREGROUND
I/flutter ( 9987): redirectRoute result
E/flutter ( 9987): [ERROR:flutter/lib/ui/ui_dart_state.cc(209)] Unhandled Exception: Null check operator used on a null value
E/flutter ( 9987): #0 Navigator.of (package:flutter/src/widgets/navigator.dart:2561)
E/flutter ( 9987): #1 LocalNotificationService.initialize.<anonymous closure> (package:learningapp/local_notification_service.dart:20)
E/flutter ( 9987): #2 LocalNotificationService.initialize.<anonymous closure> (package:learningapp/local_notification_service.dart:16)
E/flutter ( 9987): #3 AndroidFlutterLocalNotificationsPlugin._handleMethod (package:flutter_local_notifications/src/platform_flutter_local_notifications.dart:460)
E/flutter ( 9987): #4 MethodChannel._handleAsMethodCall (package:flutter/src/services/platform_channel.dart:414)
E/flutter ( 9987): #5 MethodChannel.setMethodCallHandler.<anonymous closure> (package:flutter/src/services/platform_channel.dart:407)
E/flutter ( 9987): #6 _DefaultBinaryMessenger.setMessageHandler.<anonymous closure> (package:flutter/src/services/binding.dart:377)
E/flutter ( 9987): #7 _DefaultBinaryMessenger.setMessageHandler.<anonymous closure> (package:flutter/src/services/binding.dart:374)
E/flutter ( 9987): #8 _invoke2.<anonymous closure> (dart:ui/hooks.dart:190)
E/flutter ( 9987): #9 _rootRun (dart:async/zone.dart:1426)
E/flutter ( 9987): #10 _CustomZone.run (dart:async/zone.dart:1328)
E/flutter ( 9987): #11 _CustomZone.runGuarded (dart:async/zone.dart:1236)
E/flutter ( 9987): #12 _invoke2 (dart:ui/hooks.dart:189)
E/flutter ( 9987): #13 _ChannelCallbackRecord.invoke (dart:ui/channel_buffers.dart:42)
E/flutter ( 9987): #14 _Channel.push (dart:ui/channel_buffers.dart:132)
E/flutter ( 9987): #15 ChannelBuffers.push (dart:ui/channel_buffers.dart:329)
E/flutter ( 9987): #16 PlatformDispatcher._dispatchPlatformMessage (dart:ui/platform_dispatcher.dart:583)
E/flutter ( 9987): #17 _dispatchPlatformMessage (dart:ui/hooks.dart:89)
E/flutter ( 9987):
UPDATES on FCM getInitialMessage() returns null
I have solved the problem with an alternative solution for the GetX problem.
i've been getting error "null chack opreator used on a null value"
whenever im trying to pull value from my firestore database using provider and change notifier
here's my user_provider.dart
import 'package:flutter/widgets.dart';
import 'package:test_app/models/user.dart';
import 'package:test_app/resources/auth_methods.dart';
class UserProvider with ChangeNotifier {
User? _user;
final AuthMethods _authMethods = AuthMethods();
User get getUser => _user!;
Future<void> refreshUser() async {
User user = await _authMethods.getUserDetails();
_user = user;
notifyListeners();
}
}
and here's my home.dart where im pulling data from firestore
import 'package:bottom_navy_bar/bottom_navy_bar.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:test_app/components/background.dart';
import 'package:test_app/providers/user_provider.dart';
import 'package:test_app/models/user.dart' as model;
// import 'dart:html';
class Home extends StatefulWidget {
const Home({Key? key}) : super(key: key);
#override
State<Home> createState() => _HomeState();
}
class _HomeState extends State<Home> {
#override
void initState() {
super.initState();
addData();
}
addData() async {
UserProvider _userProvider =
Provider.of<UserProvider>(context, listen: false);
await _userProvider.refreshUser();
}
#override
Widget build(BuildContext context) {
model.User? user = Provider.of<UserProvider>(context).getUser;
return Scaffold(body: Text(user.email),);
}
}
here's my main.dart for incase
import 'dart:ffi';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:test_app/pages/home.dart';
import 'package:test_app/pages/login.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:test_app/pages/signup.dart';
import 'package:test_app/providers/user_provider.dart';
import 'package:test_app/utilis/routes.dart';
void main() async {
// ignore: prefer_const_constructors
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp(MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
ChangeNotifierProvider(
create: (_) => UserProvider(),
)
],
child: MaterialApp(
// home: Home(),
debugShowCheckedModeBanner: false,
themeMode: ThemeMode.dark,
darkTheme: ThemeData(brightness: Brightness.light),
theme: ThemeData(
primarySwatch: Colors.deepPurple,
fontFamily: GoogleFonts.mcLaren().fontFamily,
appBarTheme: AppBarTheme(
color: Colors.white,
elevation: 0.0,
iconTheme: IconThemeData(color: Colors.black),
)),
// ignore: prefer_const_constructors
home: StreamBuilder(
stream: FirebaseAuth.instance.authStateChanges(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.active) {
// Checking if the snapshot has any data or not
if (snapshot.hasData) {
// if snapshot has data which means user is logged in then we check the width of screen and accordingly display the screen layout
return const Home();
} else if (snapshot.hasError) {
return const Login();
}
}
// means connection to future hasnt been made yet
if (snapshot.connectionState == ConnectionState.waiting) {
return const Center(
child: CircularProgressIndicator(),
);
}
return const Login();
})),
);
}
}
my debug console
══╡ EXCEPTION CAUGHT BY WIDGETS LIBRARY
╞═══════════════════════════════════════════════════════════
The following _CastError was thrown building Home(dirty, dependencies:
[_InheritedProviderScope<UserProvider?>], state: _HomeState#17895):
Null check operator used on a null value
The relevant error-causing widget was:
Home Home:file:///C:/Users/abc/Desktop/flutter/test_app/lib/main.dart:53:34
When the exception was thrown, this was the stack:
#0 UserProvider.getUser (package:test_app/providers/user_provider.dart:9:28)
#1 _HomeState.build (package:test_app/pages/home.dart:36:59)
#2 StatefulElement.build (package:flutter/src/widgets/framework.dart:4870:27)
#3 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:4754:15)
#4 StatefulElement.performRebuild (package:flutter/src/widgets/framework.dart:4928:11)
#5 Element.rebuild (package:flutter/src/widgets/framework.dart:4477:5)
#6 ComponentElement._firstBuild (package:flutter/src/widgets/framework.dart:4735:5)
#7 StatefulElement._firstBuild (package:flutter/src/widgets/framework.dart:4919:11)
#8 ComponentElement.mount (package:flutter/src/widgets/framework.dart:4729:5)
#9 Element.inflateWidget (package:flutter/src/widgets/framework.dart:3790:14)
#10 Element.updateChild (package:flutter/src/widgets/framework.dart:3524:20)
#11 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:4780:16)
#12 StatefulElement.performRebuild (package:flutter/src/widgets/framework.dart:4928:11)
#13 Element.rebuild (package:flutter/src/widgets/framework.dart:4477:5)
#14 BuildOwner.buildScope (package:flutter/src/widgets/framework.dart:2659:19)
#15 WidgetsBinding.drawFrame (package:flutter/src/widgets/binding.dart:882:21)
#16 RendererBinding._handlePersistentFrameCallback (package:flutter/src/rendering/binding.dart:363:5)
#17 SchedulerBinding._invokeFrameCallback (package:flutter/src/scheduler/binding.dart:1144:15)
#18 SchedulerBinding.handleDrawFrame (package:flutter/src/scheduler/binding.dart:1081:9)
#19 SchedulerBinding.scheduleWarmUpFrame.<anonymous closure> (package:flutter/src/scheduler/binding.dart:862:7)
(elided 11 frames from class _RawReceivePortImpl, class _Timer, dart:async, and dart:async-patch)
my app runs fine but before opnening it shows a redscreen with the error null check opreator used on a null value and after a sec it goes out and it keep working the same but debug shows me the following error in debugconsole
You should write User get getUser => _user ?? User();
instead of User get getUser => _user!;
in my user_provider.dart
Or you can write User _user = User();
instead of User? _user;
My code show error Firebase has not been correctly initialized.?
**Usually this means you've attempted to use a Firebase service before calling `Firebase.initializeApp`.
E/flutter (25616):
E/flutter (25616): View the documentation for more information: https://firebase.flutter.dev/docs/overview#initialization
E/flutter (25616):
E/flutter (25616): #0 MethodChannelFirebase.initializeApp (package:firebase_core_platform_interface/src/method_channel/method_channel_firebase.dart:99:9)
E/flutter (25616): <asynchronous suspension>
E/flutter (25616): #1 Firebase.initializeApp (package:firebase_core/src/firebase.dart:42:31)
E/flutter (25616): <asynchronous suspension>
E/flutter (25616): #2 main (package:a_store_online_shop/main.dart:11:3)
E/flutter (
25616): <asynchronous suspension>
E/flutter (25616):
It is our main.dart file which in occure error code firebase has not been correctly initialized.....................................................................................................................................................?
main.dart
import 'dart:async';
import 'package:firebase_core/firebase_core.dart';
import 'package:a_store_online_shop/Screens/main_screen.dart';
import 'package:a_store_online_shop/Screens/on_boarding_screen.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:get_storage/get_storage.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
await GetStorage.init();
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 MaterialApp(
debugShowCheckedModeBanner: false,
// title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const SplashScreen(),
initialRoute: SplashScreen.id,
routes: {
SplashScreen.id: (context) => const SplashScreen(),
OnBoardingScreen.id: (context) => const OnBoardingScreen(),
},
);
}
}
class SplashScreen extends StatefulWidget {
const SplashScreen({Key? key}) : super(key: key);
static const String id = "splash-screen";
#override
State<SplashScreen> createState() => _SplashScreenState();
}
class _SplashScreenState extends State<SplashScreen> {
final store = GetStorage();
#override
void initState() {
Timer(const Duration(seconds: 3), () {
bool? _boarding = store.read('onBoarding');
_boarding == null
? Navigator.pushReplacementNamed(context, OnBoardingScreen.id)
: _boarding == true
? Navigator.pushReplacementNamed(context, MainScreen.id)
: Navigator.pushReplacementNamed(context, OnBoardingScreen.id);
});
super.initState();
}
#override
Widget build(BuildContext context) {
SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge, overlays: []);
return Scaffold(
backgroundColor: Colors.black,
body: Center(
child: Image.asset("assets/images/logo.jpg"),
),
);
}
}
So for everybody else getting this error I figured it out myself, for IOS, the line
Firebase.initializeApp()
it needs an option:
Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform)
for that follow the instructions on this link:
https://firebase.flutter.dev/docs/cli
I can't figure out why I get this error when trying to run this widget test. I just want to mock a user that is in the database so when I type in an email, I either go to my PasswordFormLogin widget if the user email matches the user email that is in the mock user or then go to my RegistrationLoginForm widget.
Debug Console:
══╡ EXCEPTION CAUGHT BY FLUTTER TEST FRAMEWORK ╞══════════════════════════════════════════════════
The following NoSuchMethodError was thrown running a test:
Class 'MockFirebaseAuth' has no instance method 'fetchSignInMethodsForEmail' with matching
arguments.
Receiver: Instance of 'MockFirebaseAuth'
Tried calling: fetchSignInMethodsForEmail("bob#thebuilder.com")
Found: fetchSignInMethodsForEmail(String) => Future<List<String>>
When the exception was thrown, this was the stack:
#0 Object.noSuchMethod (dart:core-patch/object_patch.dart:54:5)
#1 MockFirebaseAuth.noSuchMethod (package:firebase_auth_mocks/src/firebase_auth_mocks_base.dart:70:56)
#2 MockFirebaseAuth.fetchSignInMethodsForEmail (package:firebase_auth/src/firebase_auth.dart:226:24)
#3 ApplicationState.verifyEmail (file:///C:/Users/calvo/Documents/flutter/projects/freegapp/test/LoginFlow_widget_test.dart:92:32)
#4 LoginFlow.build.<anonymous closure> (package:freegapp/LoginFlow.dart:57:45)
#5 _EmailFormState.build.<anonymous closure> (package:freegapp/src/LoginFlowStuff/EmailFormLogin.dart:73:40)
#6 _EmailFormState.build.<anonymous closure> (package:freegapp/src/LoginFlowStuff/EmailFormLogin.dart:64:32)
#7 _InkResponseState._handleTap (package:flutter/src/material/ink_well.dart:989:21)
#8 GestureRecognizer.invokeCallback (package:flutter/src/gestures/recognizer.dart:182:24)
#9 TapGestureRecognizer.handleTapUp (package:flutter/src/gestures/tap.dart:607:11)
#10 BaseTapGestureRecognizer._checkUp (package:flutter/src/gestures/tap.dart:296:5)
#11 BaseTapGestureRecognizer.handlePrimaryPointer (package:flutter/src/gestures/tap.dart:230:7)
#12 PrimaryPointerGestureRecognizer.handleEvent (package:flutter/src/gestures/recognizer.dart:475:9)
#13 PointerRouter._dispatch (package:flutter/src/gestures/pointer_router.dart:93:12)
#14 PointerRouter._dispatchEventToRoutes.<anonymous closure> (package:flutter/src/gestures/pointer_router.dart:138:9)
#15 _LinkedHashMapMixin.forEach (dart:collection-patch/compact_hash.dart:397:8)
#16 PointerRouter._dispatchEventToRoutes (package:flutter/src/gestures/pointer_router.dart:136:18)
#17 PointerRouter.route (package:flutter/src/gestures/pointer_router.dart:122:7)
#18 GestureBinding.handleEvent (package:flutter/src/gestures/binding.dart:439:19)
#19 GestureBinding.dispatchEvent (package:flutter/src/gestures/binding.dart:419:22)
#20 RendererBinding.dispatchEvent (package:flutter/src/rendering/binding.dart:287:11)
#21 GestureBinding._handlePointerEventImmediately (package:flutter/src/gestures/binding.dart:374:7)
#22 GestureBinding.handlePointerEvent (package:flutter/src/gestures/binding.dart:338:5)
#23 TestWidgetsFlutterBinding.handlePointerEvent (package:flutter_test/src/binding.dart:507:13)
#24 WidgetTester.sendEventToBinding.<anonymous closure> (package:flutter_test/src/widget_tester.dart:792:15)
#25 WidgetTester.sendEventToBinding.<anonymous closure> (package:flutter_test/src/widget_tester.dart:791:39)
#28 TestAsyncUtils.guard (package:flutter_test/src/test_async_utils.dart:71:41)
#29 WidgetTester.sendEventToBinding (package:flutter_test/src/widget_tester.dart:791:27)
#30 TestGesture.up.<anonymous closure> (package:flutter_test/src/test_pointer.dart:392:24)
#31 TestGesture.up.<anonymous closure> (package:flutter_test/src/test_pointer.dart:390:39)
#34 TestAsyncUtils.guard (package:flutter_test/src/test_async_utils.dart:71:41)
#35 TestGesture.up (package:flutter_test/src/test_pointer.dart:390:27)
#36 WidgetController.tapAt.<anonymous closure> (package:flutter_test/src/controller.dart:278:21)
<asynchronous suspension>
<asynchronous suspension>
(elided 5 frames from dart:async and package:stack_trace)
The test description was:
EmailFormLogin
══════════════════════════════════════════════════════════════════════════════════════════════════
LoginFlow_widget_test.dart:
import 'package:flutter_test/flutter_test.dart';
import 'package:firebase_auth_mocks/firebase_auth_mocks.dart';
import 'package:flutter/material.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:provider/provider.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:freegapp/LoginFlow.dart';
final tUser = MockUser(
isAnonymous: false,
uid: 'T3STU1D',
email: 'bob#thebuilder.com',
displayName: 'Bob Builder',
phoneNumber: '0800 I CAN FIX IT',
photoURL: 'http://photos.url/bobbie.jpg',
refreshToken: 'some_long_token',
);
void main() {
// TextField widgets require a Material widget ancestor.
// In material design, most widgets are conceptually "printed" on a sheet of material. In Flutter's
// material library, that material is represented by the Material widget. It is the Material widget
// that renders ink splashes, for instance. Because of this, many material library widgets require that
// there be a Material widget in the tree above them.
// To introduce a Material widget, you can either directly include one, or use a widget that contains
// Material itself, such as a Card, Dialog, Drawer, or Scaffold.
testWidgets('EmailFormLogin', (WidgetTester tester) async {
await tester.pumpWidget(ChangeNotifierProvider(
create: (context) => ApplicationState(),
builder: (context, _) => MaterialApp(
title: 'Freegap',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: Consumer<ApplicationState>(
builder: (context, appState, _) => LoginFlow(
email: appState.email,
loginState: appState.loginState,
startLoginFlow: appState.startLoginFlow,
verifyEmail: appState.verifyEmail,
signInWithEmailAndPassword:
appState.signInWithEmailAndPassword,
cancelRegistration: appState.cancelRegistration,
registerAccount: appState.registerAccount,
signOut: appState.signOut,
key: Key('LoginFlow'))))));
expect(find.byKey(Key('EmailFormLogin')), findsOneWidget);
// Enter 'bob#thebuilder.com' into the TextField.
await tester.enterText(find.byType(TextField), 'bob#thebuilder.com');
await tester.tap(find.byType(ElevatedButton));
expect(find.byKey(Key('EmailFormLogin')), findsOneWidget);
});
}
class ApplicationState extends ChangeNotifier {
ApplicationState() {
init();
}
final auth = MockFirebaseAuth(mockUser: tUser);
Future<void> init() async {
await Firebase.initializeApp();
auth.userChanges().listen((user) {
if (user != null) {
_loginState = ApplicationLoginState.loggedIn;
} else {
_loginState = ApplicationLoginState.loggedOut;
}
notifyListeners();
});
}
ApplicationLoginState _loginState = ApplicationLoginState.loggedOut;
ApplicationLoginState get loginState => _loginState;
String? _email;
String? get email => _email;
void startLoginFlow() {
_loginState = ApplicationLoginState.emailAddress;
notifyListeners();
}
void verifyEmail(
String email,
void Function(FirebaseAuthException e) errorCallback,
) async {
try {
var methods = await auth.fetchSignInMethodsForEmail(email);
if (methods.contains('password')) {
_loginState = ApplicationLoginState.password;
} else {
_loginState = ApplicationLoginState.register;
}
_email = email;
notifyListeners();
} on FirebaseAuthException catch (e) {
errorCallback(e);
}
}
void signInWithEmailAndPassword(
String email,
String password,
void Function(FirebaseAuthException e) errorCallback,
) async {
try {
await auth.signInWithEmailAndPassword(
email: email,
password: password,
);
} on FirebaseAuthException catch (e) {
errorCallback(e);
}
}
void cancelRegistration() {
_loginState = ApplicationLoginState.loggedOut;
notifyListeners();
}
void registerAccount(String email, String displayName, String password,
void Function(FirebaseAuthException e) errorCallback) async {
try {
var credential = await auth.createUserWithEmailAndPassword(
email: email, password: password);
await credential.user!.updateDisplayName(displayName);
} on FirebaseAuthException catch (e) {
errorCallback(e);
}
}
void signOut() {
auth.signOut();
}
}
LoginFlow.dart:
import 'package:flutter/material.dart';
import 'package:freegapp/src/LoginFlowStuff/EmailFormLogin.dart';
import 'package:freegapp/src/LoginFlowStuff/PasswordFormLogin.dart';
import 'package:freegapp/Sell.dart';
import 'package:freegapp/src/LoginFlowStuff/RegisterFormLogin.dart';
import 'package:freegapp/src/style_widgets.dart';
enum ApplicationLoginState {
loggedOut,
emailAddress,
register,
password,
loggedIn,
}
class LoginFlow extends StatelessWidget {
const LoginFlow({
required this.loginState,
required this.email,
required this.startLoginFlow,
required this.verifyEmail,
required this.signInWithEmailAndPassword,
required this.cancelRegistration,
required this.registerAccount,
required this.signOut,
Key? key,
}) : super(key: key);
final ApplicationLoginState loginState;
final String? email;
final void Function() startLoginFlow;
// typedef myFunction = final void Function(String email, void Function(Exception e) error,);
final void Function(
String email,
void Function(Exception e) error,
) verifyEmail; // myFunction verifyEmail() = {}
final void Function(
String email,
String password,
void Function(Exception e) error,
) signInWithEmailAndPassword;
final void Function() cancelRegistration;
final void Function(
String email,
String displayName,
String password,
void Function(Exception e) error,
) registerAccount;
final void Function() signOut;
#override
Widget build(BuildContext context) {
switch (loginState) {
case ApplicationLoginState.loggedOut:
return EmailFormLogin(
key: Key('EmailFormLogin'),
callback: (email) => verifyEmail(
email, (e) => _showErrorDialog(context, 'Invalid email', e)));
case ApplicationLoginState.password:
return PasswordFormLogin(
key: Key('PasswordFormLogin'),
email: email!,
login: (email, password) {
signInWithEmailAndPassword(email, password,
(e) => _showErrorDialog(context, 'Failed to sign in', e));
},
);
case ApplicationLoginState.register:
return RegisterFormLogin(
key: Key('RegisterFormLogin'),
email: email!,
cancel: () {
cancelRegistration();
},
registerAccount: (
email,
displayName,
password,
) {
registerAccount(
email,
displayName,
password,
(e) =>
_showErrorDialog(context, 'Failed to create account', e));
},
);
case ApplicationLoginState.loggedIn:
return Sell(
logout: () {
signOut();
},
key: Key('Sell'),
);
default:
return Row(
children: const [
Text("Internal error, this shouldn't happen..."),
],
);
}
}
}
void _showErrorDialog(BuildContext context, String title, Exception e) {
showDialog<void>(
context: context,
builder: (context) {
return AlertDialog(
title: Text(
title,
style: const TextStyle(fontSize: 24),
),
content: SingleChildScrollView(
child: ListBody(
children: <Widget>[
Text(
'${(e as dynamic).message}',
style: const TextStyle(fontSize: 18),
),
],
),
),
actions: <Widget>[
StyledButton(
onPressed: () {
Navigator.of(context).pop();
},
child: const Text(
'OK',
style: TextStyle(color: Colors.deepPurple),
),
),
],
);
},
);
}
EmailFormLogin.dart:
import 'package:flutter/material.dart';
import 'package:freegapp/src/style_widgets.dart';
class EmailFormLogin extends StatefulWidget {
const EmailFormLogin({
required this.callback,
Key? key,
}) : super(key: key);
final void Function(String email) callback;
#override
_EmailFormState createState() => _EmailFormState();
}
class _EmailFormState extends State<EmailFormLogin> {
// Create a global key that uniquely identifies the Form widget
// and allows validation of the form.
//
// Note: This is a `GlobalKey<FormState>`,
// not a GlobalKey<_EmailFormState>.
final _formKey = GlobalKey<FormState>(debugLabel: '_EmailFormState');
final _controller = TextEditingController();
#override
void dispose() {
_controller.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(children: <Widget>[
Form(
key: _formKey,
child: Column(
children: <Widget>[
const Header('Sign in / Register'),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 24),
child: TextFormField(
controller: _controller,
keyboardType: TextInputType.emailAddress,
decoration: const InputDecoration(
hintText: 'Enter your email',
prefixIcon: Icon(Icons.mail),
),
validator: (value) {
if (value!.isEmpty) {
return 'Enter your email address';
}
return null;
},
),
),
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
Padding(
padding: const EdgeInsets.symmetric(
vertical: 16.0, horizontal: 30),
child: ElevatedButton(
onPressed: () async {
// The FormState class contains the validate() method.
// When the validate() method is called, it runs the validator() function
// for each text field in the form. If everything looks good,
// the validate() method returns true. If any text field contains errors,
// the validate() method rebuilds the form to display any error messages and returns false.
// add ! to assert that it isn’t null (and to throw an exception if it is).
if (_formKey.currentState!.validate()) {
// If the form is valid,
widget.callback(_controller
.text); // call to parent with current string the user is editing
}
},
child: const Text('NEXT'),
),
),
],
),
],
),
),
]));
}
}
The firebase_auth_mocks package currently does not support mocking the fetchSignInMethodsForEmail() method.
A suggestion to get past the error is to replace this line:
var methods = await auth.fetchSignInMethodsForEmail(email);
with something like this:
var methods = await Future.value(['password']);
I am trying to use firebase analytics to follow the user navigation and pressing on some buttons i made a _currentScreen() function which i call init state in the class in which the user navigates to
here is the function
Future<Null> _currentScreen() async {
await widget.analytics.setCurrentScreen(
screenName: 'second screen view', screenClassOverride: 'SecondScreenView');
}
and i made another function which is _sendAnalytics which i call when the user click on a button; here is the function:
Future<Null> _sendAnalytics() async {
await widget.analytics
.logEvent(name: 'launchhhh', parameters: <String, dynamic>{});
}
I have several problems:
1st- in the initstate in the second screen class it's warning :is method overrides a method annotated as #mustCallSuper in 'State', but does not invoke the overridden method.
2nd- when i run the code and press the button of navigations i have this
[ERROR:flutter/shell/common/shell.cc(181)] Dart Error: Unhandled exception:
E/flutter (12744): NoSuchMethodError: The method 'logEvent' was called on null.
E/flutter (12744): Receiver: null
E/flutter (12744): Tried calling: logEvent(name: "launchhhh", parameters: _LinkedHashMap len:0)
E/flutter (12744): #0 Object.noSuchMethod (dart:core/runtime/libobject_patch.dart:50:5)
E/flutter (12744): #1 FirstScreenState._sendAnalytics (file:///C:/Users/pc/Desktop/wedn2/wedn2/lib/h.dart:40:10)
3- Also, when i comment the _sendanalytics function
NoSuchMethodError: The method 'setCurrentScreen' was called on null.
E/flutter (15612): Receiver: null
E/flutter (15612): Tried calling: setCurrentScreen(screenClassOverride: "SecondScreenView", screenName: "second screen view")
E/flutter (15612): #0 Object.noSuchMethod (dart:core/runtime/libobject_patch.dart:50:5)
E/flutter (15612): #1 SecondScreenState._currentScreen (file:///C:/Users/pc/Desktop/wedn2/wedn2/lib/h.dart:91:28)
E/flutter (15612): <asynchronous suspension>
E/flutter (15612): #2 SecondScreenState.initState (file:///C:/Users/pc/Desktop/wedn2/wedn2/lib/h.dart:88:5)
E/flutter (15612): #3 StatefulElement._firstBuild (package:flutter/src/widgets/framework.dart:3787:58)
E/flutter (15612): #4 ComponentElement.mount (package:flutter/src/widgets/framework.dart:3653:5)
I don't know where is the problem any help? and here is my whole code:
import 'package:flutter/material.dart';
import 'package:firebase_analytics/firebase_analytics.dart';
import 'package:firebase_analytics/observer.dart';
import 'dart:async';
class MyAppf extends StatelessWidget {
static FirebaseAnalytics analytics = new FirebaseAnalytics();
static FirebaseAnalyticsObserver observer =
new FirebaseAnalyticsObserver(analytics: analytics);
#override
Widget build(BuildContext context) {
return new MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Flutter Demo',
theme: new ThemeData(
primarySwatch: Colors.blue,
),
navigatorObservers: <NavigatorObserver>[observer],
// home: new WallScreen(analytics: analytics, observer: observer),
home: new FirstScreen(),
);
}
}
class FirstScreen extends StatefulWidget {
final FirebaseAnalytics analytics;
final FirebaseAnalyticsObserver observer;
FirstScreen({this.analytics, this.observer});
#override
FirstScreenState createState() => new FirstScreenState();
}
class FirstScreenState extends State<FirstScreen> {
Future<Null> _sendAnalytics() async {
await widget.analytics
.logEvent(name: 'launchhhh', parameters: <String, dynamic>{});
}
final FirebaseAnalytics analytics;
final FirebaseAnalyticsObserver observer;
FirstScreenState({this.analytics, this.observer});
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('First Screen'),
),
body: Center(
child: RaisedButton(
child: Text('Launch screen'),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => SecondScreen()),
);
_sendAnalytics();
},
),
),
);
}
}
class SecondScreen extends StatefulWidget {
final FirebaseAnalytics analytics;
final FirebaseAnalyticsObserver observer;
SecondScreen({this.analytics, this.observer});
#override
SecondScreenState createState() => new SecondScreenState();
}
class SecondScreenState extends State<SecondScreen> {
#override
void initState() {
_currentScreen();
}
Future<Null> _currentScreen() async {
await widget.analytics.setCurrentScreen(
screenName: 'second screen view', screenClassOverride: 'SecondScreenView');
}
Future<Null> _sendAnalyticsback() async {
await widget.analytics
.logEvent(name: 'back', parameters: <String, dynamic>{});
}
final FirebaseAnalytics analytics;
final FirebaseAnalyticsObserver observer;
SecondScreenState({this.analytics, this.observer});
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Second Screen"),
),
body: Center(
child: RaisedButton(
onPressed: () {
Navigator.pop(context);
_sendAnalyticsback();
},
child: Text('Go back!'),
),
),
);
}
}
EDIT I passed my var analytics to Widgets to be
home:new FirstScreen(analytics: analytics),
Navigator.push(
context,
MaterialPageRoute(builder: (context) => SecondScreen(analytics:analytics)),
instead of :
home:new FirstScreen(),
Navigator.push(
context,
MaterialPageRoute(builder: (context) => SecondScreen()),
But still it's the same error calling on null
You need to pass your var analytics to your Widgets.
new FirstScreen(),
new SecondScreen(),
Should be :
new FirstScreen(analytics: analytics),
new SecondScreen(analytics: analytics),
Do not use static members as they are intended to be accessed through the class itself. A cealner way of what you are trying to do is declare MyApp as a SatetfulWidget , now you analytics and observer can be private members of State class and use initState to initialize them, see the following example:
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => new _MyAppState();
}
class _MyAppState extends State<MyApp> {
FirebaseAnalytics _analytics;
FirebaseAnalyticsObserver _observer ;
#override
void initState() {
_analytics = FirebaseAnalytics();
_observer = FirebaseAnalyticsObserver(analytics: _analytics);
super.initState();
}
#override
Widget build(BuildContext context) {
return Container();
}
}
If you want to keep your widget Stateless you can initialize your observer in the constructor's initializer list, this requires the analytics field to be passed a value when MyApp is created MyApp(analytics:FirebaseAnalytics()):
class MyApp extends StatelessWidget {
final FirebaseAnalytics analytics;
final FirebaseAnalyticsObserver observer;
MyApp({Key key,#required this.analytics})
: observer = FirebaseAnalyticsObserver(analytics: analytics),
super(key: key);
#override
Widget build(BuildContext context) {
return Container();
}
}