Using `Provider` in Firebase Auth does not work - android

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.

Related

stateless Widget Keeps Rebuilding

I'M currently working on a to-do app following a tutorial
this is the checkbox part
I'm trying to lift it's state up the tree, but when I do so the stateless widget just keeps rebuilding non stop ,
I tried adding key , adding const converting it to stateful but nothing seems to work
it also doesn't provide me with any kind of error or exceptions messages
note : when I set the onChecked to null the statelessWidget runs twice when it should only build once..
below is the code:
import 'package:flutter/material.dart';
class TaskTile extends StatefulWidget {
#override
_TaskTileState createState() => _TaskTileState();
}
class _TaskTileState extends State {
bool isChecked = false;
#override
Widget build(BuildContext context) {
return ListTile(
title: Text(
"Complete Todoey",
style: TextStyle(
decoration: isChecked == true
? TextDecoration.lineThrough
: TextDecoration.none),
),
trailing: TaskCheckBox(
checkBoxState: isChecked,
toggleCheckBoxState:
(bool checkBoxState) {
Future.delayed(Duration.zero,
() async {
setState(() {
print(
"===================$isChecked==============$checkBoxState======================");
isChecked = checkBoxState;
});
});
}),
);
}
}
class TaskCheckBox extends StatelessWidget {
const TaskCheckBox(
{Key? key,
required this.checkBoxState,
required this.toggleCheckBoxState})
: super(key: key);
final bool checkBoxState;
final Function toggleCheckBoxState;
#override
Widget build(BuildContext context) {
print(
"=====================================$checkBoxState+++++++++++++++++++++++++++++++++++++++++");
return Checkbox(
value: checkBoxState,
onChanged:
null, // toggleCheckBoxState(checkBoxState),
);
}
}
a snip from the console output :
Performing hot restart...
Syncing files to device Android SDK built for x86...
I/flutter ( 3401): ===================false==============false======================
I/flutter ( 3401): =====================================false+++++++++++++++++++++++++++++++++++++++++
I/flutter ( 3401): ===================false==============false======================
I/flutter ( 3401): =====================================false+++++++++++++++++++++++++++++++++++++++++
I/flutter ( 3401): ===================false==============false======================
I/flutter ( 3401): =====================================false+++++++++++++++++++++++++++++++++++++++++
I/flutter ( 3401): ===================false==============false======================
I/flutter ( 3401): =====================================false+++++++++++++++++++++++++++++++++++++++++
I/flutter ( 3401): ===================false==============false======================
I/flutter ( 3401): =====================================false+++++++++++++++++++++++++++++++++++++++++
a snip from the output when onChanged is set to null :
Restarted application in 2,694ms.
I/flutter ( 3401): =====================================false+++++++++++++++++++++++++++++++++++++++++
I/flutter ( 3401): =====================================false+++++++++++++++++++++++++++++++++++++++++
The purpose of setState is to tell the framework that a variable in the state has changed and the widget needs to be rebuilt to reflect that change. So calling setState calls the build function again, which in your case recalls your Future, which calls setState again, which triggers build and so on.
To fix this you could get rid of async function with Future.delayed since it's not needed there at all as well as putting print outside your setstate function.

Flutter: Looking up a deactivated widget's ancestor is unsafe

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.

Flutter: Dependency Injecting using Multiprovider and Consumer in the same tree

I'm trying to inject instances of services (that have been created in the same tree level) into another provider. But down the tree when accessing the provider, I get ProviderNotFoundException exception.
In the following code NotificationService depends on AuthService. Which needs to be passed in the constructor. Hence I inject it using Consumer and Provider.value as mentioned in the docs: https://pub.dev/documentation/provider/latest/provider/Consumer-class.html
Here is the pseudo-code:
return MultiProvider(
providers: [
Provider<AuthService>(
create: (ctx) => AuthService(_storage),
dispose: (ctx, v) => v.dispose(),
),
Consumer<AuthService>(
builder: (context, v, child) {
return Provider.value(
value: Provider<NotificationService>(
create: (ctx) => NotificationService(v),
dispose: (ctx, v) => v.dispose(),
),
child: child
);
},
)
],
child: MyApp()
);
Somewhere down the tree line, When trying to access the NotificationService instance, I get ProviderNotFoundException:
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
final NotificationService _ns = Provider.of<NotificationService>(context);
}
}
Error:
I/flutter ( 4614): ══╡ EXCEPTION CAUGHT BY WIDGETS LIBRARY ╞═══════════════════════════════════════════════════════════
I/flutter ( 4614): The following ProviderNotFoundException was thrown building MyApp(dirty, dependencies:
I/flutter ( 4614): [_DefaultInheritedProviderScope<AuthService>]):
I/flutter ( 4614): Error: Could not find the correct Provider<NotificationService> above this MyApp Widget
I/flutter ( 4614):
I/flutter ( 4614): To fix, please:
I/flutter ( 4614):
I/flutter ( 4614): * Ensure the Provider<NotificationService> is an ancestor to this MyApp Widget
I/flutter ( 4614): * Provide types to Provider<NotificationService>
I/flutter ( 4614): * Provide types to Consumer<NotificationService>
I/flutter ( 4614): * Provide types to Provider.of<NotificationService>()
I/flutter ( 4614): * Ensure the correct `context` is being used.
I/flutter ( 4614):
I don't fully understand this, and I'm pretty sure there is a mistake in the above code. What am I doing wrong?
The way you used Provider.value is invalid. But you don't actually need Consumer+Provider. You can do:
MultiProvider(
providers: [
Provider(create: (_) => A()),
Provider(create: (context) => B(Provider.of<A>(context, listen: false)),
],
)

Flutter : Could not find the correct Provider

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

Flutter Custom Keyboard ('Fake' soft keyboard)

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

Categories

Resources