I am tying to implement Firebase Authentication in my flutter app, with the help of provider. First, I am checking whether the user is already signed in, if yes, i AM sending him to the home screen. Else I am sending him to the Login screen.
Please check my code below.
main.dart
import 'package:flutter/material.dart';
import 'package:customer/services/auth.dart';
import 'package:provider/provider.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return ChangeNotifierProvider(
child: MaterialApp(
title: 'Customer App',
home: AuthWrapper(),
routes: {
'/account': (context) => AccountPage(),
},
), create: (BuildContext context) {
AuthService();
},
);
}
}
auth.dart
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
class AuthService with ChangeNotifier{
final FirebaseAuth _auth = FirebaseAuth.instance;
FirebaseUser _user=null;
//Sign in with username and password
Future signInWithEmail(String email, String password) async {
FirebaseUser user;
try {
AuthResult result = await _auth.signInWithEmailAndPassword(
email: email, password: password);
user = result.user;
if (user != null) {
print("Sign in success: " + user.email);
_user = user;
} else {
print("sign in failed");
_user = null;
}
} catch (e) {
print(e.toString());
}
finally{
notifyListeners();
}
}
//Get the current user
FirebaseUser getCurrentUser() {
return _user;
}
}
auth_wrapper.dart
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import 'package:customer/pages/home.dart';
import 'package:customer/pages/login.dart';
import 'package:customer/services/auth.dart';
import 'package:provider/provider.dart';
class AuthWrapper extends StatelessWidget {
AuthWrapper() {}
#override
Widget build(BuildContext context) {
var currentUser =Provider.of<AuthService>(context, listen: false).getCurrentUser();
if (currentUser == null) {
return LoginPage();
} else {
return HomePage();
}
}
}
Unfortunately, when I run this app, i get the following error.
I/flutter ( 3364): The following ProviderNotFoundException was thrown building AuthWrapper(dirty):
I/flutter ( 3364): Error: Could not find the correct Provider<AuthService> above this AuthWrapper Widget
I/flutter ( 3364):
I/flutter ( 3364): To fix, please:
I/flutter ( 3364):
I/flutter ( 3364): * Ensure the Provider<AuthService> is an ancestor to this AuthWrapper Widget
I/flutter ( 3364): * Provide types to Provider<AuthService>
I/flutter ( 3364): * Provide types to Consumer<AuthService>
I/flutter ( 3364): * Provide types to Provider.of<AuthService>()
I/flutter ( 3364): * Ensure the correct `context` is being used.
I/flutter ( 3364):
I/flutter ( 3364): If none of these solutions work, please file a bug at:
I/flutter ( 3364): https://github.com/rrousselGit/provider/issues
I/flutter ( 3364):
I/flutter ( 3364): The relevant error-causing widget was:
[38;5;248mI/flutter ( 3364): AuthWrapper[39;49m
I/flutter ( 3364):
I/flutter ( 3364): When the exception was thrown, this was the stack:
[38;5;248mI/flutter ( 3364): #0 Provider.of[39;49m
[38;5;248mI/flutter ( 3364): #1 AuthWrapper.build[39;49m
[38;5;244mI/flutter ( 3364): #2 StatelessElement.build[39;49m
[38;5;244mI/flutter ( 3364): #3 ComponentElement.performRebuild[39;49m
[38;5;244mI/flutter ( 3364): #4 Element.rebuild[39;49m
[38;5;244mI/flutter ( 3364): #5 ComponentElement._firstBuild[39;49m
[38;5;244mI/flutter ( 3364): #6 ComponentElement.mount[39;49m
[38;5;244mI/flutter ( 3364): #7 Element.inflateWidget[39;49m
[38;5;244mI/flutter ( 3364): #8 Element.updateChild[39;49m
[38;5;244mI/flutter ( 3364): #9 SingleChildRenderObjectElement.mount[3
I am newly trying out the provider patter, whats really going on here?
I also has a second question. Well, provider is a pattern. But in flutter we know things get little out of context so, does that mean all business logic (REST API Related) need to take place in provider ? Or else, only use in places where you need the observer pattern behaviour?
I think value provider could be best solution for your case. Following minimal code help you to understand more.
I think it is totally dependent on you, if you think data is not that much big then you can avoid using provider because it create lot’s of other boilerplate code. However i also suggest you to use flutter_bloc package.
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return StreamProvider<User>.value(
value: AuthService().user,
child: MaterialApp(
home: FireBaseWrapper(),
),
);
}
}
class FireBaseWrapper extends StatelessWidget {
#override
Widget build(BuildContext context) {
final _user = Provider.of<User>(context);
print(_user);
if (_user != null)
return FireBase();
else
return Register();
}
}
class AuthService {
final FirebaseAuth _auth = FirebaseAuth.instance;
User firebaseUserToUser(FirebaseUser _user) {
return _user != null ? User(userId: _user.uid) : null;
}
//stream of User
Stream<User> get user {
return _auth.onAuthStateChanged.map(firebaseUserToUser);
}
// firebase sign in Anonymously
Future signInAnonymously() async {
try {
AuthResult _authResult = await _auth.signInAnonymously();
FirebaseUser _user = _authResult.user;
return firebaseUserToUser(_user);
} catch (e) {
return null;
}
}
Future signInWithIdPassword({String email, String password}) async {
try {
AuthResult _authResult = await _auth.signInWithEmailAndPassword(
email: email, password: password);
FirebaseUser _user = _authResult.user;
return firebaseUserToUser(_user);
} catch (e) {
return null;
}
}
Future signUpWithIdPassword({String email, String password}) async {
try {
AuthResult _authResult = await _auth.createUserWithEmailAndPassword(
email: email.trim(), password: password);
FirebaseUser _user = _authResult.user;
return firebaseUserToUser(_user);
} catch (e) {
return null;
}
}
// sign out
Future signOut() async {
try {
return await _auth.signOut();
} catch (e) {
return null;
}
}
}
Related
I am trying to check firebase database using flutter to see if user who is logged in is registered or not so checking to see if firstname exist in user database but what is happening is that the activeListeners function is always executing after rmn() function which should not happen. activeListeners should execute before rmn() .
Here is my code below:
import 'package:flutter/material.dart';
import 'package:after_layout/after_layout.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:firebase_database/firebase_database.dart';
import 'package:ayto_driver_x/signup.dart';
import 'package:ayto_driver_x/mainpage.dart';
import 'package:ayto_driver_x/login.dart';
import 'package:fluttertoast/fluttertoast.dart';
void main() => runApp(new MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'After Layout - Good Example',
home: new LoginState(),
);
}
}
class LoginState extends StatefulWidget {
#override
HomeScreenState createState() => new HomeScreenState();
}
class HomeScreenState extends State<LoginState> with AfterLayoutMixin<LoginState> {
FirebaseAuth auth = FirebaseAuth.instance;
String srm="";
final _database=FirebaseDatabase.instance.ref();
#override
void initState() {
// TODO: implement initState
super.initState();
activeListeners();
}
void activeListeners(){
final User? user=auth.currentUser;
final id=user?.uid ;
_database.child('Driver/${user?.uid}/FirstName').onValue.listen((event) {
final Object? description=event.snapshot.value ?? false;
setState(() {
print('DescriptionLS=$description');
srm='$description';
print('SRMx1=$srm');
});
});
}
#override
Widget build(BuildContext context) {
activeListeners();
return new Scaffold(body: new Container(color: Colors.white));
}
#override
void afterFirstLayout(BuildContext context) {
// Calling the same function "after layout" to resolve the issue.
activeListeners();
rmn();
}
void rmn()
{
FirebaseAuth.instance
.authStateChanges()
.listen((User? user) {
if (user == null) {
print('User is currently signed out!');
Navigator.pushAndRemoveUntil(
context,
MaterialPageRoute(
builder: (context) => LoginScreen(),
),
(e) => false,
);
}
else {
activeListeners();
print('User is signed in!');
print('SRMxc=$srm');
Fluttertoast.showToast(
msg: srm,
toastLength: Toast.LENGTH_SHORT,
gravity: ToastGravity.CENTER,
timeInSecForIosWeb: 1,
backgroundColor: Colors.red,
textColor: Colors.white,
fontSize: 16.0,
);
if(srm.compareTo('false')==0) {
Navigator.pushReplacement(
context,
MaterialPageRoute(
builder: (context) => const SignupScreen(),
),
);
}
else {
Navigator.pushAndRemoveUntil(
context,
MaterialPageRoute(
builder: (context) => const MainPage(),
),
(e) => false,
);
}
}
});
}
}
Log File :
I/flutter ( 5748): User is signed in!
I/flutter ( 5748): SRMxc=
I/flutter ( 5748): DescriptionLS=false
I/flutter ( 5748): SRMx1=false
I/flutter ( 5748): DescriptionLS=false
I/flutter ( 5748): SRMx1=false
I/flutter ( 5748): DescriptionLS=false
I/flutter ( 5748): SRMx1=false
I/flutter ( 5748): DescriptionLS=false
I/flutter ( 5748): SRMx1=false
Please suggest me a solution to execute activeListeners() before rmn().
I am trying to implement a simple login/signup screen on flutter, connected to firebase.
The flow is supposed to be as follows:
Main => Wrapper
Wrappper => if user==NULL, then Authenticate, else Home
Authenticate => Login or Signup
The issue is occuring in the Wrapper file. When the code is run, initially the user will be NULL and has to go to the Login screen. However, my code goes directly to the Home screen.
The codes for each file are as follows:
main.dart
void main() async {
await Firebase.initializeApp();
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return StreamProvider<MyUser>.value(
value: AuthService().user,
initialData: null,
child: MaterialApp(
home: Wrapper(),
debugShowCheckedModeBanner: false,
)
);
}
}
wrapper.dart
class Wrapper extends StatelessWidget {
#override
Widget build(BuildContext context) {
final user = Provider.of<MyUser>(context);
if (user == null) {
return Authenticate();
} else {
//return PatientHome();
}
}
}
authenticate.dart
class Authenticate extends StatefulWidget {
#override
_AuthenticateState createState() => _AuthenticateState();
}
class _AuthenticateState extends State<Authenticate> {
bool showSignIn = true;
void toggleView() {
setState(() => showSignIn = !showSignIn);
}
#override
Widget build(BuildContext context) {
if (showSignIn) {
return Login(toggleView: toggleView);
} else {
return Register(toggleView: toggleView);
}
}
}
auth.dart
class AuthService {
final FirebaseAuth _auth = FirebaseAuth.instance;
// create user obj based on firebase user
MyUser _userFromFirebaseUser(User user) {
return user != null ? MyUser(uid: user.uid) : null;
}
// auth change user stream
Stream<MyUser> get user {
return _auth.authStateChanges()
//.map((FirebaseUser user) => _userFromFirebaseUser(user));
.map(_userFromFirebaseUser);
}
// sign in with email and password
Future signInWithEmailAndPassword(String email, String password) async {
try {
UserCredential result = await _auth.signInWithEmailAndPassword(email: email, password: password);
User user = result.user;
return _userFromFirebaseUser(user);
} catch (error) {
print(error.toString());
return null;
}
}
// register with email and password
Future registerWithEmailAndPassword(String email, String password) async {
try {
UserCredential result = await _auth.createUserWithEmailAndPassword(email: email, password: password);
User user = result.user;
return _userFromFirebaseUser(user);
} catch (error) {
print(error.toString()+"oollala");
return null;
}
}
// sign out
Future signOut() async {
try {
return await _auth.signOut();
} catch (error) {
print(error.toString());
return null;
}
}
}
register.dart
class Register extends StatefulWidget {
final Function toggleView;
Register({this.toggleView});
#override
RegisterPatient createState() => RegisterPatient();
}
class RegisterPatient extends State<Register> {
final AuthService _auth = AuthService();
final _formKey = GlobalKey<FormState>();
bool loading = false;
String email = '';
String password = '';
String error = '';
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
appBar: AppBar(
backgroundColor: Colors.deepPurple,
elevation: 0.0,
title: Text('Register'),
actions: <Widget>[
TextButton.icon(
onPressed: () => widget.toggleView(),
icon: Icon(Icons.person),
label: Text('Register as Patient'))
],
),
body: Container(
padding: EdgeInsets.symmetric(vertical: 20.0, horizontal: 50.0),
child: Form(
key: _formKey,
child: Column(
children: <Widget>[
SizedBox(height: 20.0),
TextFormField(
validator: (val) => val.isEmpty ? 'Enter email' : null,
onChanged: (val) {
setState(() => email = val);
},
),
SizedBox(height: 20.0),
TextFormField(
obscureText: true,
validator: (val) => val.length < 6 ? 'Enter a password 6+ chars long' : null,
onChanged: (val) {
setState(() => password = val);
},
),
SizedBox(height: 20.0),
RaisedButton(
color: Colors.deepPurple,
child: Text(
'Register',
style: TextStyle(color: Colors.white),
),
onPressed: () async {
if(_formKey.currentState.validate()){
setState(() => loading = true);
dynamic result = await _auth.registerWithEmailAndPassword(email, password);
if(result == null) {
setState(() {
error = 'Email/password incorrect';
loading = false;
});
} else {
Navigator.of(context).pop();
loading = false;
}
}
}
),
SizedBox(height: 12.0),
Text(
error,
style: TextStyle(color: Colors.red, fontSize: 14.0),
)
],
),
),
),
);
}
}
login.dart
similar to register.dart
The issue is that whenever I run this code, it always takes me to HomePage, even though it is supposed to go through the Authenticate file as user is supposed to be NULL.
EDIT 1:
I thought it was directing to Homepage rather than to Authenticate, however my screen is simply white. It does not do anything other than that.
Any idea what could be the issue?
EDIT 2:
The code runs like so, shows a white screen, and never terminated unless I forcibly do so. I changed from emulator to my phone, but it still the same. Any idea what could be the issue? I'm fairly new to flutter so I don't understand the error...
Launching lib\main.dart on sdk gphone x86 arm in debug mode...
Running Gradle task 'assembleDebug'...
√ Built build\app\outputs\flutter-apk\app-debug.apk.
Debug service listening on ws://127.0.0.1:64181/vsky5JbFCKM=/ws
Syncing files to device sdk gphone x86 arm...
E/flutter ( 8057): [ERROR:flutter/lib/ui/ui_dart_state.cc(199)] Unhandled Exception: Null check operator used on a null value
E/flutter ( 8057): #0 MethodChannel.binaryMessenger (package:flutter/src/services/platform_channel.dart:142:86)
E/flutter ( 8057): #1 MethodChannel._invokeMethod (package:flutter/src/services/platform_channel.dart:148:36)
E/flutter ( 8057): #2 MethodChannel.invokeMethod (package:flutter/src/services/platform_channel.dart:331:12)
E/flutter ( 8057): #3 MethodChannel.invokeListMethod (package:flutter/src/services/platform_channel.dart:344:41)
E/flutter ( 8057): #4 MethodChannelFirebase._initializeCore (package:firebase_core_platform_interface/src/method_channel/method_channel_firebase.dart:30:37)
E/flutter ( 8057): #5 MethodChannelFirebase.initializeApp (package:firebase_core_platform_interface/src/method_channel/method_channel_firebase.dart:77:13)
E/flutter ( 8057): #6 Firebase.initializeApp (package:firebase_core/src/firebase.dart:41:47)
E/flutter ( 8057): #7 main (package:hospicare_mobile/main.dart:14:18)
E/flutter ( 8057): #8 _runMainZoned.<anonymous closure>.<anonymous closure> (dart:ui/hooks.dart:142:25)
E/flutter ( 8057): #9 _rootRun (dart:async/zone.dart:1354:13)
E/flutter ( 8057): #10 _CustomZone.run (dart:async/zone.dart:1258:19)
E/flutter ( 8057): #11 _runZoned (dart:async/zone.dart:1789:10)
E/flutter ( 8057): #12 runZonedGuarded (dart:async/zone.dart:1777:12)
E/flutter ( 8057): #13 _runMainZoned.<anonymous closure> (dart:ui/hooks.dart:138:5)
E/flutter ( 8057): #14 _delayEntrypointInvocation.<anonymous closure> (dart:isolate-patch/isolate_patch.dart:283:19)
E/flutter ( 8057): #15 _RawReceivePortImpl._handleMessage (dart:isolate-patch/isolate_patch.dart:184:12)
E/flutter ( 8057):
I think using shared preferences might help for saving some data inside the app even though the app is closed or restarted by the user .Even you can retrieve it again and if it has something in it you can navigate to homepage , if it is null you can navigate to authenticate page.
Visit :
https://pub.dev/packages/shared_preferences
Follow the steps to use it:
Add the following inside your authenticate page or signin page
SharedPreferences
data=awaitSharedPreferences.getInstance();
data.setString('your_key',your_data);
Add the following inside the main function before runapp function and make the main function as async
SharedPreferences data = await
SharedPreferences.getInstance();
var your_variable = data.getString('your_key');
I know that there are already two posts regarding this question, but I could not solve my issue looking at them. Probably because in my case the issue is different.
The code is the following. I want to load some data from the database while showing a loading page. After the data is loaded, I initialize the provider with the loaded data and then I move into a different page.
This code does not need to be in a StatefulWidget, but I try to put it in a StatefulWidget to solve the issue, but without success.
class _InitDBDataState extends State<_InitDBData> {
#override
Widget build(BuildContext context) {
_fetchData(context);
return const Center(child: const CircularProgressIndicator());
}
Future<void> _fetchData(BuildContext context) async {
print('fetching data...');
print('context: $context');
final initData = await DBService.service.getInitialData();
print('Data fetched');
print('context: $context');
Provider.of<DataProvider>(context, listen: false).init(initData);
Navigator.of(context).pushReplacementNamed(MainScreen.routeName);
}
}
I do not have any error if the application runs from scratch, but when I am doing a "Hot Reload" I get the following error pretty often, and it is annoying since I need to restart the application for each small change in the code.
I/flutter ( 9596): fetching data...
I/flutter ( 9596): context: _InitDBData(dirty, state: _InitDBDataState#46860)
I/flutter ( 9596): fetching data...
I/flutter ( 9596): context: _InitDBData(dirty, state: _InitDBDataState#55124)
I/flutter ( 9596): Data fetched
I/flutter ( 9596): context: _InitDBData
E/flutter ( 9596): [ERROR:flutter/lib/ui/ui_dart_state.cc(157)] Unhandled Exception: Looking up a deactivated widget's ancestor is unsafe.
E/flutter ( 9596): At this point the state of the widget's element tree is no longer stable.
E/flutter ( 9596): 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 ( 9596): #0 Element._debugCheckStateIsActiveForAncestorLookup.<anonymous closure>
package:flutter/…/widgets/framework.dart:3508
E/flutter ( 9596): #1 Element._debugCheckStateIsActiveForAncestorLookup
package:flutter/…/widgets/framework.dart:3522
E/flutter ( 9596): #2 Element.getElementForInheritedWidgetOfExactType
package:flutter/…/widgets/framework.dart:3588
E/flutter ( 9596): #3 Provider.of
package:provider/src/provider.dart:221
E/flutter ( 9596): #4 _InitDBDataState._fetchData
package:productive_diary/initScreen.dart:46
E/flutter ( 9596): <asynchronous suspension>
E/flutter ( 9596): #5 _InitDBDataState.build
I don't know why "fetching data..." is printed twice, and I have no clue on how to solve the issue.
I thought the issue was solved with the solution of Saman Salehi, but working in debug mode I had the same exception in the _fetchData function, that now is called in the function initState()
Exception has occurred.
FlutterError (Looking up a deactivated widget's ancestor is unsafe.
At this point the state of the widget's element tree is no longer stable.
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.)
I got another error after applying the edits suggested by Stewie Griffin
.
The error is on the line Provider.of<DataProvider>(context, listen: false).init(initData);
I got it during a hot reload. It seems less common than the other error, so the answer of Stewie Griffin surely improved the stability of my Stewie Griffin
E/flutter (23815): [ERROR:flutter/lib/ui/ui_dart_state.cc(157)] Unhandled Exception: NoSuchMethodError: The getter 'owner' was called on null.
E/flutter (23815): Receiver: null
E/flutter (23815): Tried calling: owner
E/flutter (23815): #0 Object.noSuchMethod (dart:core-patch/object_patch.dart:53:5)
E/flutter (23815): #1 Provider.of
package:provider/src/provider.dart:193
E/flutter (23815): #2 _InitDBDataState._fetchData
package:productive_diary/initScreen.dart:49
E/flutter (23815): <asynchronous suspension>
E/flutter (23815): #3 _InitDBDataState.initState
Could you please help me?
First of all, never call async methods inside of build as mentioned. Build method is constantly rebuilt and it will cause your fetch method to be repeated like an infinite loop. After you fix that, you still get the same error because of this part:
Navigator.of(context).pushReplacementNamed(MainScreen.routeName);
You shouldn't call Navigator during build. Here is what you need to do:
Add this line to the top of your file to use SchedulerBinding:
import 'package:flutter/scheduler.dart';
Wrap Navigator with SchedulerBinding to wait for completing the state before navigating to another screen. Then, call your async method inside of initState.
class _InitDBDataState extends State<_InitDBData> {
#override
void initState() {
// Call your async method here
_fetchData();
super.initState();
}
Future<void> _fetchData() async {
print('fetching data...');
print('context: $context');
final initData = await DBService.service.getInitialData();
print('Data fetched');
print('context: $context');
Provider.of<DataProvider>(context, listen: false).init(initData);
// Wrap Navigator with SchedulerBinding to wait for rendering state before navigating
SchedulerBinding.instance.addPostFrameCallback((_) {
Navigator.of(context).pushReplacementNamed(MainScreen.routeName);
});
}
#override
Widget build(BuildContext context) {
return Center(child: CircularProgressIndicator());
}
}
Tip: You don't need to pass the context in a Stateful Widget because you can access it from everywhere.
You shouldn't use the build method for anything other than building UI. build can be called at any time even when it's not on the screen.
I would move the _fetchData to the initState so it wouldn't cause any conflict at the build method.
class _InitDBDataState extends State<_InitDBData> {
#override
void initState() {
super.initState();
_fetchData(context);
}
#override
Widget build(BuildContext context) {
return const Center(child: const CircularProgressIndicator());
}
Future<void> _fetchData(BuildContext context) async {
print('fetching data...');
print('context: $context');
final initData = await DBService.service.getInitialData();
print('Data fetched');
print('context: $context');
Provider.of<DataProvider>(context, listen: false).init(initData);
Navigator.of(context).pushReplacementNamed(MainScreen.routeName);
}
}
Navigator.pushReplacement(context,MaterialPageRoute(builder:(context) => WelcomeScreen()),);
|^| .wrap this code SchedulerBinding.instance!.addPostFrameCallback on above code.
like this below:
SchedulerBinding.instance!.addPostFrameCallback((_) {
Navigator.pushReplacement(
context,
MaterialPageRoute(builder: (context) => WelcomeScreen()),
);
});
Schedule a callback for the end of this frame.
Does not request a new frame.
This callback is run during a frame, just after the persistent frame
callbacks (which is when the main rendering pipeline has been
flushed). If a frame is in progress and post-frame callbacks haven't
been executed yet, then the registered callback is still executed
during the frame. Otherwise, the registered callback is executed
during the next frame.
The callbacks are executed in the order in which they have been added.
Post-frame callbacks cannot be unregistered. They are called exactly
once.
Before:
After
SampleCode Here i use rive: ^0.8.1 dartpackage
import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';
import 'package:rive/rive.dart';
void main() {
WidgetsFlutterBinding.ensureInitialized();
runApp(MaterialApp(home: SimpleAnimation()));
}
setdata(BuildContext context) async {
await Future.delayed(const Duration(seconds: 5), () {
SchedulerBinding.instance!.addPostFrameCallback((_) {
Navigator.pushReplacement(
context,
MaterialPageRoute(builder: (context) => WelcomeScreen()),
);
});
});
}
class SimpleAnimation extends StatelessWidget {
const SimpleAnimation({Key? key, this.loading}) : super(key: key);
final bool? loading;
#override
Widget build(BuildContext context) {
setdata(context);
return Scaffold(
body: Center(
child: Container(
height: 200,
width: 200,
child: RiveAnimation.network(
'https://cdn.rive.app/animations/vehicles.riv',
),
),
),
);
}
}
class WelcomeScreen extends StatelessWidget {
const WelcomeScreen({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Center(
child: Container(
child: Text(
"HOME PAGE",
style: TextStyle(fontSize: 50),
),
),
),
);
}
}
if it is caused by accessing ScaffoldMessenger by context then putting it inside a try catch will resolve context error.
try{
ScaffoldMessenger.of(_scaffoldKey.currentContext!).showSnackBar();
} catch(e){
print(e);
}
For this probleme you can use one of this two solutions :
first: add scheduler like this :
SchedulerBinding.instance!.addPostFrameCallback((_) {
Navigator.of(context).pushReplacementNamed(MainScreen.routeName);
});
second: add future delayed with some milliseconds like this :
Future.delayed(Duration(microseconds: 200))
.then((value) {
Navigator.of(context).pushReplacementNamed(MainScreen.routeName);
});
!!! But if you a lot of change of state in the same time this solutions may still give the same error.
if is the case try to use the second solution by augmenting the duration.
I am tying to implement Firebase Authentication in my Flutter app, with the help of provider. First, I am checking whether the user is already signed in. If yes, I AM sending him to the home screen. Else I am sending him to the Login screen.
Please check my code below.
main.dart
import 'package:flutter/material.dart';
import 'package:customer/services/auth.dart';
import 'package:provider/provider.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return ChangeNotifierProvider(
child: MaterialApp(
title: 'Customer App',
home: AuthWrapper(),
routes: {
'/account': (context) => AccountPage(),
},
), create: (BuildContext context) {
AuthService();
},
);
}
}
auth.dart
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
class AuthService with ChangeNotifier{
final FirebaseAuth _auth = FirebaseAuth.instance;
FirebaseUser _user=null;
//Sign in with username and password
Future signInWithEmail(String email, String password) async {
FirebaseUser user;
try {
AuthResult result = await _auth.signInWithEmailAndPassword(
email: email, password: password);
user = result.user;
if (user != null) {
print("Sign in success: " + user.email);
_user = user;
} else {
print("sign in failed");
_user = null;
}
} catch (e) {
print(e.toString());
}
finally{
notifyListeners();
}
}
//Get the current user
FirebaseUser getCurrentUser() {
return _user;
}
}
auth_wrapper.dart
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import 'package:customer/pages/home.dart';
import 'package:customer/pages/login.dart';
import 'package:customer/services/auth.dart';
import 'package:provider/provider.dart';
class AuthWrapper extends StatelessWidget {
AuthWrapper() {}
#override
Widget build(BuildContext context) {
var currentUser =Provider.of<AuthService>(context, listen: false).getCurrentUser();
if (currentUser == null) {
return LoginPage();
} else {
return HomePage();
}
}
}
Unfortunately, when I run this app, i get the following error.
I/flutter ( 3364): The following ProviderNotFoundException was thrown building AuthWrapper(dirty):
I/flutter ( 3364): Error: Could not find the correct Provider<AuthService> above this AuthWrapper Widget
I/flutter ( 3364):
I/flutter ( 3364): To fix, please:
I/flutter ( 3364):
I/flutter ( 3364): * Ensure the Provider<AuthService> is an ancestor to this AuthWrapper Widget
I/flutter ( 3364): * Provide types to Provider<AuthService>
I/flutter ( 3364): * Provide types to Consumer<AuthService>
I/flutter ( 3364): * Provide types to Provider.of<AuthService>()
I/flutter ( 3364): * Ensure the correct `context` is being used.
I/flutter ( 3364):
I/flutter ( 3364): If none of these solutions work, please file a bug at:
I/flutter ( 3364): https://github.com/rrousselGit/provider/issues
I/flutter ( 3364):
I/flutter ( 3364): The relevant error-causing widget was:
[38;5;248mI/flutter ( 3364): AuthWrapper[39;49m
I/flutter ( 3364):
I/flutter ( 3364): When the exception was thrown, this was the stack:
[38;5;248mI/flutter ( 3364): #0 Provider.of[39;49m
[38;5;248mI/flutter ( 3364): #1 AuthWrapper.build[39;49m
[38;5;244mI/flutter ( 3364): #2 StatelessElement.build[39;49m
[38;5;244mI/flutter ( 3364): #3 ComponentElement.performRebuild[39;49m
[38;5;244mI/flutter ( 3364): #4 Element.rebuild[39;49m
[38;5;244mI/flutter ( 3364): #5 ComponentElement._firstBuild[39;49m
[38;5;244mI/flutter ( 3364): #6 ComponentElement.mount[39;49m
[38;5;244mI/flutter ( 3364): #7 Element.inflateWidget[39;49m
[38;5;244mI/flutter ( 3364): #8 Element.updateChild[39;49m
[38;5;244mI/flutter ( 3364): #9 SingleChildRenderObjectElement.mount[3
I am newly trying out the provider patter, whats really going on here?
You can do the following.
Change the create: (BuildContext context) to create: (_)
.You are not currently using the BuildContext thus it can be changed to _ which is just cleaner code
Secondly it does not seem that your function is returning the value to the create (I might be wrong on this). This is why the functional instantiation works better.
Examples from the provider package
ChangeNotifierProvider(create: (_) => Counter()),
Your code changes
So Change this
create: (BuildContext context) {
AuthService();
},
To this
create: (_) => AuthService();
References
Using Underscore
Provider Package - Examples taken from here
If any one can explain the process of this better please assist.
I'm working on a finance app and I'd like a custom textentry field and keyboard for currency entry with an inbuilt calculator.
I've tried using a BottomSheet, both persistent and modal. The modal behaviour is ideal, but it always shows a barrier. The persistent one is what I have now, using a focus node to show and hide it, but it's throwing strange errors:
I/flutter (30319): The following NoSuchMethodError was thrown while dispatching notifications for FocusNode:
I/flutter (30319): The method 'removeLocalHistoryEntry' was called on null.
I/flutter (30319): Receiver: null
I/flutter (30319): Tried calling: removeLocalHistoryEntry(Instance of 'LocalHistoryEntry')
I/flutter (30319):
I/flutter (30319): When the exception was thrown, this was the stack:
I/flutter (30319): #0 Object.noSuchMethod (dart:core/runtime/libobject_patch.dart:46:5)
I/flutter (30319): #1 LocalHistoryEntry.remove (package:flutter/src/widgets/routes.dart:296:12)
I/flutter (30319): #2 _NumpadFieldState.initState.<anonymous closure> (file:///D:/code/financepie/lib/widgets/numpad/numpadfield.dart:30:32)
...
In any case, the bottom sheet behaviour (dragging down) isn't really ideal to copy the android/ios soft keyboard. Any better solutions? Current code below:
import 'package:flutter/material.dart';
import 'numpad.dart';
class NumpadField extends StatefulWidget {
#override
_NumpadFieldState createState() {
return new _NumpadFieldState();
}
}
class _NumpadFieldState extends State<NumpadField> {
ValueNotifier<List<String>> state;
FocusNode focusNode;
PersistentBottomSheetController bottomSheetController;
#override initState() {
super.initState();
state = ValueNotifier<List<String>>([]);
state.addListener(() => setState((){}));
focusNode = FocusNode();
focusNode.addListener(() {
print(focusNode);
if (focusNode.hasFocus) {
bottomSheetController = showBottomSheet(
context: context,
builder: (context) => Numpad(state: state),
);
} else {
bottomSheetController?.close(); ///this line causing the error
}
});
}
#override dispose() {
state.dispose();
focusNode.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () {
FocusScope.of(context).requestFocus(focusNode);
},
child: Container(
child: Text(state.value.fold<String>("", (str, e) => "$str $e")),
constraints: BoxConstraints.expand(height: 24.0),
decoration: BoxDecoration(
border: BorderDirectional(bottom: BorderSide())
),
),
);
}
}
bottomSheetController?.close(); is nullable. Since the line causes the error, you can add a null-check to prevent this issue.
else {
if(bottomSheetController != null)
bottomSheetController!.close();
}