Flutter app crashes on firebase phone number authentication in Android platform - android

I have implemented Firebase Phone authentication in my Flutter project. The process crashes after I clicked "login", the app crashes and lost connection.
Below are the codes (get from the internet):
main.dart
import 'package:flutter/material.dart';
import 'loginpage.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: LoginPage(),
);
}
}
loginpage.dart
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import 'dart:async';
class LoginPage extends StatefulWidget {
#override
_LoginPageState createState() => _LoginPageState();
}
class _LoginPageState extends State<LoginPage> {
String phoneNo, smssent, verificationId;
Future<void> verifyPhone() async {
final PhoneCodeAutoRetrievalTimeout autoRetrieve = (String verID) {
this.verificationId = verID;
};
final PhoneCodeSent smsCodeSent = (String verId, [int forceCodeResent]) {
this.verificationId = verId;
smsCodeDialogue(context).then((value) {
print("Code Sent");
});
};
final PhoneVerificationCompleted verifiedSuccess = (AuthCredential auth) {};
final PhoneVerificationFailed verifyFailed = (AuthException e) {
print('${e.message}');
};
await FirebaseAuth.instance.verifyPhoneNumber(
phoneNumber: phoneNo,
timeout: const Duration(seconds: 5),
verificationCompleted: verifiedSuccess,
verificationFailed: verifyFailed,
codeSent: smsCodeSent,
codeAutoRetrievalTimeout: autoRetrieve,
);
}
Future<bool> smsCodeDialogue(BuildContext context) {
return showDialog(
context: context,
barrierDismissible: false,
builder: (BuildContext context) {
return new AlertDialog(
title: Text('Enter OTP'),
content: TextField(
onChanged: (value) {
this.smssent = value;
},
),
contentPadding: EdgeInsets.all(10.0),
actions: <Widget>[
FlatButton(
onPressed: () {
FirebaseAuth.instance.currentUser().then((user) {
if (user != null) {
Navigator.of(context).pop();
Navigator.push(
context,
MaterialPageRoute(builder: (context) => LoginPage()),
);
} else {
Navigator.of(context).pop();
signIn(smssent);
}
});
},
child: Text(
"done",
style: TextStyle(color: Colors.white),
),
),
],
);
});
}
Future<bool> signIn(String smsCode) async {
final AuthCredential credential = PhoneAuthProvider.getCredential(
verificationId: verificationId,
smsCode: smsCode,
);
await FirebaseAuth.instance.signInWithCredential(credential).then((user) {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => LoginPage(),
),
);
}).catchError((e) {
print(e);
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Center(
child: Text('PhoneNumber Login'),
),
),
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Padding(
padding: const EdgeInsets.all(16.0),
child: TextField(
decoration: InputDecoration(
hintText: "Enter your phone number",
),
onChanged: (value) {
this.phoneNo = value;
}),
),
SizedBox(
height: 10.0,
),
RaisedButton(
onPressed: verifyPhone,
child: Text(
"verify",
style: TextStyle(color: Colors.white),
),
elevation: 7.0,
color: Colors.blue,
)
],
),
);
}
}
this is the error
Lost connection to device.
Exited (sigterm)
May I know what is the problem? Anyone would like to help me to solve this...

I solved the problem by simply adding below line into app/build.gradle dependencies.
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation 'com.google.firebase:firebase-auth'
implementation "androidx.browser:browser:1.2.0"
}

Related

flutter webview navigation menu is not working

I'm trying to make a webview app for my website and use the following starter code
import 'package:flutter/material.dart';
import 'package:webview_flutter/webview_flutter.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Welcome to Flutter1',
home: Scaffold(
appBar: AppBar(
title: const Text('Welcome to Flutter1'),
),
body: WebView(
initialUrl: "URL.com",
javascriptMode: JavascriptMode.unrestricted,
),
),
);
}
}
PROBLEM
While the app opens the page, the navigation menu (three dots) do not respond when I click.
What can I do?
EDIT
I also tried to allow all mixed content using flutter_inappwebview
import 'package:flutter/material.dart';
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Welcome to Flutter2',
home: Scaffold(
appBar: AppBar(
title: const Text('Welcome to Flutter2'),
),
body: InAppWebView(
initialUrlRequest: URLRequest(url: Uri.parse("www.URL.com")),
initialOptions: InAppWebViewGroupOptions(
android: AndroidInAppWebViewOptions(
mixedContentMode: AndroidMixedContentMode.MIXED_CONTENT_COMPATIBILITY_MODE
)
),
),
),
);
}
}
Still the dots are not working.
you have to have a controller for your Navigation- and WebView-Menu
final Completer<WebViewController> _controller =
Completer<WebViewController>();
then in your build methode:
Scaffold(
appBar: AppBar(
title: Text('Webview'),
actions: <Widget>[
NavigationControls(_controller.future),
WebViewMenu(_controller.future),
],
}
),
body: Builder(....)...
these are the classes from https://pub.dev/packages/webview_flutter/example
class WebViewMenu extends StatelessWidget {
WebViewMenu(this.controller);
final Future<WebViewController> controller;
final CookieManager cookieManager = CookieManager();
#override
Widget build(BuildContext context) {
return FutureBuilder<WebViewController>(
future: controller,
builder:
(BuildContext context, AsyncSnapshot<WebViewController> controller) {
if (controller.hasError) {
return Text('Controller has Error');
}
return PopupMenuButton<MenuOptions>(
onSelected: (MenuOptions value) {
switch (value) {
case MenuOptions.showUserAgent:
_onShowUserAgent(controller.data!, context);
break;
case MenuOptions.listCookies:
_onListCookies(controller.data!, context);
break;
case MenuOptions.clearCookies:
_onClearCookies(context);
break;
case MenuOptions.addToCache:
_onAddToCache(controller.data!, context);
break;
case MenuOptions.listCache:
_onListCache(controller.data!, context);
break;
case MenuOptions.clearCache:
_onClearCache(controller.data!, context);
break;
case MenuOptions.navigationDelegate:
_onNavigationDelegateExample(controller.data!, context);
break;
}
},
itemBuilder: (BuildContext context) => <PopupMenuItem<MenuOptions>>[
PopupMenuItem<MenuOptions>(
value: MenuOptions.showUserAgent,
child: const Text('Show user agent'),
enabled: controller.hasData,
),
const PopupMenuItem<MenuOptions>(
value: MenuOptions.listCookies,
child: Text('List cookies'),
),
const PopupMenuItem<MenuOptions>(
value: MenuOptions.clearCookies,
child: Text('Clear cookies'),
),
const PopupMenuItem<MenuOptions>(
value: MenuOptions.addToCache,
child: Text('Add to cache'),
),
const PopupMenuItem<MenuOptions>(
value: MenuOptions.listCache,
child: Text('List cache'),
),
const PopupMenuItem<MenuOptions>(
value: MenuOptions.clearCache,
child: Text('Clear cache'),
),
const PopupMenuItem<MenuOptions>(
value: MenuOptions.navigationDelegate,
child: Text('Navigation Delegate example'),
),
],
);
},
);
}
void _onShowUserAgent(
WebViewController controller, BuildContext context) async {
// Send a message with the user agent string to the Toaster JavaScript channel we registered
// with the WebView.
await controller.evaluateJavascript(
'Toaster.postMessage("User Agent: " + navigator.userAgent);');
}
void _onListCookies(
WebViewController controller, BuildContext context) async {
final String cookies =
await controller.evaluateJavascript('document.cookie');
// ignore: deprecated_member_use
Scaffold.of(context).showSnackBar(SnackBar(
content: Column(
mainAxisAlignment: MainAxisAlignment.end,
mainAxisSize: MainAxisSize.min,
children: <Widget>[
const Text('Cookies:'),
_getCookieList(cookies),
],
),
));
}
void _onAddToCache(WebViewController controller, BuildContext context) async {
await controller.evaluateJavascript(
'caches.open("test_caches_entry"); localStorage["test_localStorage"] = "dummy_entry";');
// ignore: deprecated_member_use
Scaffold.of(context).showSnackBar(const SnackBar(
content: Text('Added a test entry to cache.'),
));
}
void _onListCache(WebViewController controller, BuildContext context) async {
await controller.evaluateJavascript('caches.keys()'
'.then((cacheKeys) => JSON.stringify({"cacheKeys" : cacheKeys, "localStorage" : localStorage}))'
'.then((caches) => Toaster.postMessage(caches))');
}
void _onClearCache(WebViewController controller, BuildContext context) async {
await controller.clearCache();
// ignore: deprecated_member_use
Scaffold.of(context).showSnackBar(const SnackBar(
content: Text("Cache cleared."),
));
}
void _onClearCookies(BuildContext context) async {
final bool hadCookies = await cookieManager.clearCookies();
String message = 'There were cookies. Now, they are gone!';
if (!hadCookies) {
message = 'There are no cookies.';
}
// ignore: deprecated_member_use
Scaffold.of(context).showSnackBar(SnackBar(
content: Text(message),
));
}
void _onNavigationDelegateExample(
WebViewController controller, BuildContext context) async {
final String contentBase64 =
base64Encode(const Utf8Encoder().convert(kNavigationExamplePage));
await controller.loadUrl('data:text/html;base64,$contentBase64');
}
Widget _getCookieList(String cookies) {
if (cookies == null || cookies == '""') {
return Container();
}
final List<String> cookieList = cookies.split(';');
final Iterable<Text> cookieWidgets =
cookieList.map((String cookie) => Text(cookie));
return Column(
mainAxisAlignment: MainAxisAlignment.end,
mainAxisSize: MainAxisSize.min,
children: cookieWidgets.toList(),
);
}
}
class NavigationControls extends StatelessWidget {
const NavigationControls(this._webViewControllerFuture);
final Future<WebViewController> _webViewControllerFuture;
#override
Widget build(BuildContext context) {
return FutureBuilder<WebViewController>(
future: _webViewControllerFuture,
builder:
(BuildContext context, AsyncSnapshot<WebViewController> snapshot) {
if (snapshot.hasError) {
return Text(snapshot.error.toString());
}
if (snapshot.hasData) {
final bool webViewReady =
snapshot.connectionState == ConnectionState.done;
final WebViewController controller = snapshot.data!;
return Row(
children: <Widget>[
IconButton(
icon: const Icon(Icons.arrow_back_ios),
onPressed: !webViewReady
? null
: () async {
if (await controller.canGoBack()) {
await controller.goBack();
} else {
// ignore: deprecated_member_use
Scaffold.of(context).showSnackBar(
const SnackBar(
content: Text("No back history item")),
);
return;
}
},
),
IconButton(
icon: const Icon(Icons.arrow_forward_ios),
onPressed: !webViewReady
? null
: () async {
if (await controller.canGoForward()) {
await controller.goForward();
} else {
// ignore: deprecated_member_use
Scaffold.of(context).showSnackBar(
const SnackBar(
content: Text("No forward history item")),
);
return;
}
},
),
IconButton(
icon: const Icon(Icons.replay),
onPressed: !webViewReady
? null
: () {
controller.reload();
},
),
],
);
}
return Text('loading navigationControls');
},
);
}
}

My login screen doesn't redirect me to the new screen after I successfully login! Flutter

Hello everyone I've run into a weird bug in flutter, when I try to login it doesn't redirect me to my authenticated page(the state doesn't update) but it stays as it is, it redirects me only after I hot restart the app.
Previously it worked fine but now I don't know what is wrong with my code, if you could point my mistakes that would be help me out a bunch.
Thank you in advance
main.dart
import 'package:flutter/services.dart';
import 'package:flutter/material.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:provider/provider.dart';
import './providers/user_provider.dart';
import './screens/tab_bar_screen.dart';
import './screens/auth/auth_tab_bar_screen.dart';
import './screens/login_screen.dart';
import './screens/auth/admin/edit_project_screen.dart';
import './screens/auth/admin/register_user.dart';
import './screens/auth/project_detail_screen.dart';
import './providers/auth.dart';
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
Provider<Auth>(
create: (_) => Auth(FirebaseAuth.instance),
),
StreamProvider(
create: (context) =>
Provider.of<Auth>(context, listen: false).authStateChanges,
),
],
child: MaterialApp(
theme: ThemeData(
primaryColor: Colors.blue,
backgroundColor: Colors.blue,
accentColor: Colors.blueAccent,
brightness: Brightness.light,
accentColorBrightness: Brightness.light,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
debugShowCheckedModeBanner: false,
home: AuthenticationWrapper(),
routes: {
LoginScreen.routeName: (ctx) => LoginScreen(),
TabBarScreen.routeName: (ctx) => TabBarScreen(),
AuthTabBarScreen.routeName: (ctx) => AuthTabBarScreen(),
EditProjectScreen.routeName: (ctx) => EditProjectScreen(),
RegisterUserScreen.routeName: (ctx) => RegisterUserScreen(),
ProjectDetailScreen.routeName: (ctx) => ProjectDetailScreen(),
},
),
);
}
}
class AuthenticationWrapper extends StatelessWidget {
#override
Widget build(BuildContext context) {
final _firebaseUser = context.watch<User>();
if (_firebaseUser != null) {
return AuthTabBarScreen();
} else {
return TabBarScreen();
}
}
}
auth.dart
import 'package:firebase_auth/firebase_auth.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
class Auth {
final FirebaseAuth _firebaseAuth;
Auth(this._firebaseAuth);
final databaseReference = FirebaseFirestore.instance;
Stream<User> get authStateChanges => _firebaseAuth.authStateChanges();
//Get Current User UID
Future<String> userUid() async {
return _firebaseAuth.currentUser.uid;
}
//Get Current User
Future<void> getCurrentUser() async {
return _firebaseAuth.currentUser;
}
Future<String> login(
String email,
String password,
) async {
try {
await _firebaseAuth.signInWithEmailAndPassword(
email: email, password: password);
return 'Signed in';
} on FirebaseAuthException catch (error) {
print(error.message);
return error.message;
}
}
Future<void> signUp(
String email,
String password,
String firstName,
String lastName,
bool isAdmin,
) async {
try {
// Create a new user
final currentUser = await _firebaseAuth.createUserWithEmailAndPassword(
email: email, password: password);
// Update user
await databaseReference
.collection("users")
.doc(currentUser.user.uid)
.set({
'firstName': firstName,
'lastName': lastName,
'email': currentUser.user.email,
'isAdmin': isAdmin,
});
} on FirebaseAuthException catch (error) {
return error.message;
}
}
Future<void> logout() async {
await _firebaseAuth.signOut();
}
}
login_screen.dart
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import '../providers/auth.dart';
class LoginScreen extends StatefulWidget {
static const routeName = '/login';
#override
_LoginScreenState createState() => _LoginScreenState();
}
class _LoginScreenState extends State<LoginScreen> {
final _passwordFocusNode = FocusNode();
final _passwordController = TextEditingController();
var _isLoading = false;
final _formKey = GlobalKey<FormState>();
Map<String, String> _authData = {
'email': '',
'password': '',
};
void _showErrorDialogBox(String message) async {
return showDialog(
context: context,
builder: (ctx) => AlertDialog(
title: Text('An error occurred'),
content: Text(message),
actions: <Widget>[
FlatButton(
onPressed: () {
Navigator.of(ctx).pop();
},
child: Text('Okay'),
),
],
),
);
}
#override
void dispose() {
_passwordFocusNode.dispose();
_passwordController.dispose();
super.dispose();
}
Future<void> _submit() async {
if (!_formKey.currentState.validate()) {
// Invalid!
return;
}
_formKey.currentState.save();
setState(() {
_isLoading = true;
});
try {
await Provider.of<Auth>(context, listen: false).login(
_authData['email'],
_authData['password'],
);
} on FirebaseAuthException catch (error) {
return error.message;
} catch (error) {
const errorMessage = 'Authentication failed, please try again later.';
_showErrorDialogBox(errorMessage);
} finally {
setState(() {
_isLoading = false;
});
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Login'),
centerTitle: true,
),
body: Padding(
padding: const EdgeInsets.all(25),
child: Form(
key: _formKey,
child: SingleChildScrollView(
child: Column(
children: <Widget>[
Container(
padding: EdgeInsets.only(top: 30, bottom: 30),
child: Image.asset('assets/images/logo.png'),
),
TextFormField(
decoration: InputDecoration(
labelText: 'E-Mail',
border: OutlineInputBorder(
borderSide:
BorderSide(color: Theme.of(context).primaryColor),
),
hintText: 'Type your email address',
hintStyle: TextStyle(
fontSize: 12,
),
),
keyboardType: TextInputType.emailAddress,
textInputAction: TextInputAction.next,
focusNode: _passwordFocusNode,
validator: (value) {
Pattern pattern =
r'^(([^<>()[\]\\.,;:\s#\"]+(\.[^<>()[\]\\.,;:\s#\"]+)*)|(\".+\"))#((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$';
RegExp regex = new RegExp(pattern);
if (!regex.hasMatch(value))
return 'Enter a Valid Email';
else
return null;
},
onSaved: (value) {
_authData['email'] = value;
},
),
SizedBox(
height: 35,
),
TextFormField(
decoration: InputDecoration(
labelText: 'Password',
border: OutlineInputBorder(
borderSide:
BorderSide(color: Theme.of(context).primaryColor),
),
hintText: 'Type your password',
hintStyle: TextStyle(
fontSize: 12,
),
),
obscureText: true,
textInputAction: TextInputAction.done,
//controller: _passwordController,
validator: (value) {
if (value.isEmpty || value.length <= 5) {
return 'Password is too short!';
}
return null;
},
onSaved: (value) {
_authData['password'] = value;
},
),
SizedBox(
height: 35,
),
if (_isLoading)
CircularProgressIndicator()
else
RaisedButton(
color: Theme.of(context).primaryColor,
onPressed: _submit,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(5),
),
child: Text(
'LOGIN',
style: TextStyle(
color: Colors.white,
),
),
),
],
),
),
),
),
);
}
}
Well guys I found out what the problem was, the code that I posted on the question is indeed correct, and the problem was the way I signed out
Previous code:
ListTile(
leading: Icon(Icons.logout),
title: Text('Logout'),
onTap: () {
Navigator.of(context).pop();
Navigator.of(context)
.pushReplacementNamed(TabBarScreen.routeName);
Provider.of<Auth>(context, listen: false).logout();
},
)
Fixed code:
ListTile(
leading: Icon(Icons.logout),
title: Text('Logout'),
onTap: () {
Provider.of<Auth>(context, listen: false).logout();
},
)
The reason for this was because firebase already took care of pop() and push() to the homepage()(that I gave in main.dart), and the conflict arose when I manually tried to pop() and replace the page and redirect the user to the homepage().
Hope that was a good enough explanation.

Making direct-phone call in SPEAKER mode in Flutter

I can make direct phone calls from app using this flutter_phone_direct_caller package. But I am trying to start the phone call in SPEAKER MODE (Handsfree) as default.
How can I achieve it (direct phone call + forced speaker mode)?
PS. Although I am testing the app on android only now, the speaker function has to work on both android and ios eventually.
My code so far:
import 'package:flutter_phone_direct_caller/flutter_phone_direct_caller.dart';
void main() {
runApp(Scaffold(
body: Center(
child: RaisedButton(
onPressed: () {_callNumber('780111000');},
child: Text('Call Number'),
),
),
));
}
_callNumber(String phoneNumber) async {
bool res = await FlutterPhoneDirectCaller.callNumber(phoneNumber);
}
You can use package https://pub.dev/packages/flutter_audio_manager to set speaker mode
code snippet
res = await FlutterAudioManager.changeToSpeaker();
full example code
import 'package:flutter/material.dart';
import 'dart:async';
import 'package:flutter_audio_manager/flutter_audio_manager.dart';
void main() => runApp(MyApp());
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
AudioInput _currentInput = AudioInput("unknow", 0);
List<AudioInput> _availableInputs = [];
#override
void initState() {
super.initState();
init();
}
Future<void> init() async {
FlutterAudioManager.setListener(() async {
print("-----changed-------");
await _getInput();
setState(() {});
});
await _getInput();
if (!mounted) return;
setState(() {});
}
_getInput() async {
_currentInput = await FlutterAudioManager.getCurrentOutput();
print("current:$_currentInput");
_availableInputs = await FlutterAudioManager.getAvailableInputs();
print("available $_availableInputs");
}
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: SafeArea(
child: Padding(
padding: const EdgeInsets.all(10),
child: Column(
children: <Widget>[
Text(
"current output:${_currentInput.name} ${_currentInput.port}",
),
Divider(),
Expanded(
child: ListView.builder(
itemBuilder: (_, index) {
AudioInput input = _availableInputs[index];
return Row(
children: <Widget>[
Expanded(child: Text("${input.name}")),
Expanded(child: Text("${input.port}")),
],
);
},
itemCount: _availableInputs.length,
),
),
],
),
),
),
floatingActionButton: FloatingActionButton(
onPressed: () async {
bool res = false;
if (_currentInput.port == AudioPort.receiver) {
res = await FlutterAudioManager.changeToSpeaker();
print("change to speaker:$res");
} else {
res = await FlutterAudioManager.changeToReceiver();
print("change to receiver:$res");
}
await _getInput();
},
),
),
);
}
}

The argument type 'Widget Function(BuildContext)' cant be assigned to the parameter type 'Widget Function(BuildContext, Widget)' Error in Flutter

This is a code:
import 'package:flutter/material.dart';
import 'package:flutterapp/ui/pages/notes_home.dart';
import 'package:provider/provider.dart';
import 'package:flutterapp/ui/pages/splash.dart';
import 'package:flutterapp/ui/pages/user_info.dart';
import 'package:flutterapp/ui/pages/auth/login.dart';
import 'package:flutterapp/model/user_repository.dart';
import 'package:path/path.dart';
class HomePage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return ChangeNotifierProvider(
builder: (_) => UserRepository.instance(),
child: Consumer
// ignore: missing_return
(builder: (context, UserRepository user, _) {
// ignore: missing_return
switch (user.status){
case Status.Uninitialized:
return Splash();
case Status.Unauthenticated:
case Status.Authenticating:
return LoginPage();
case Status.Authenticated:
return NotesHomePage();
}
}),
);
}
}
The line "builder: (_) => UserRepository.instance()," in above code shows a error
The argument type 'Widget Function(BuildContext)' cant be assigned to the parameter type 'Widget Function(BuildContext, Widget)'
and whenever I run the program it shows that
Could not find the correct Providerabove this LoginPage Widget
UserRepository code:
import 'package:flutter/material.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:google_sign_in/google_sign_in.dart';
enum Status { Uninitialized, Authenticated, Authenticating, Unauthenticated }
class UserRepository with ChangeNotifier {
FirebaseAuth _auth;
FirebaseUser _user;
GoogleSignIn _googleSignIn;
Status _status = Status.Uninitialized;
UserRepository.instance()
: _auth = FirebaseAuth.instance,
_googleSignIn = GoogleSignIn() {
_auth.onAuthStateChanged.listen(_onAuthStateChanged);
}
Status get status => _status;
FirebaseUser get user => _user;
Future<bool> signIn(String email, String password) async{
try {
_status = Status.Authenticating;
notifyListeners();
await _auth.signInWithEmailAndPassword(email: email, password: password);
return true;
}catch(e){
_status = Status.Unauthenticated;
notifyListeners();
return false;
}
}
Future<bool> signUp(String email, String password) async{
try {
_status = Status.Authenticating;
notifyListeners();
await _auth.createUserWithEmailAndPassword(email: email, password: password);
return true;
}catch(e){
_status = Status.Unauthenticated;
notifyListeners();
return false;
}
}
Future<bool> signInWithGoogle() async{
try{
_status =Status.Authenticating;
notifyListeners();
final GoogleSignInAccount googleUser = await _googleSignIn.signIn();
final GoogleSignInAuthentication googleAuth =
await googleUser.authentication;
final AuthCredential credential = GoogleAuthProvider.getCredential(idToken: googleAuth.idToken, accessToken: googleAuth.accessToken);
await _auth.signInWithCredential(credential);
return true;
}catch(e) {
print(e);
_status = Status.Unauthenticated;
notifyListeners();
return false;
}
}
Future signOut() async {
_auth.signOut();
_googleSignIn.signOut();
_status = Status.Unauthenticated;
notifyListeners();
return Future.delayed(Duration.zero);
}
Future<void> _onAuthStateChanged(FirebaseUser firebaseUser) async{
if(firebaseUser == null) {
_status = Status.Unauthenticated;
}else{
_user = firebaseUser;
_status = Status.Authenticated;
}
notifyListeners();
}
}
Please help
Here is a login page code:
import 'package:flutter/material.dart';
import 'package:flutterapp/model/user_repository.dart';
import 'package:provider/provider.dart';
class LoginPage extends StatefulWidget {
#override
_LoginPageState createState() => _LoginPageState();
}
class _LoginPageState extends State<LoginPage> {
final GlobalKey<ScaffoldState> _key = GlobalKey<ScaffoldState>();
bool signInForm;
#override
void initState() {
// TODO: implement initState
super.initState();
signInForm = true;
}
#override
Widget build(BuildContext context) {
final user = Provider.of<UserRepository>(context);
return WillPopScope(
onWillPop: () async{
if(!signInForm) {
setState(() {
signInForm = true;
});
return false;
}else{
return true;
}
},
child: Scaffold(
key: _key,
backgroundColor: Colors.red,
body: SingleChildScrollView(
padding: const EdgeInsets.all(16.0),
child: SizedBox(
width: double.infinity,
child: Column(
children: <Widget>[
const SizedBox(height: kToolbarHeight),
Container(
decoration: BoxDecoration(
color: Colors.white,
shape: BoxShape.circle,
),
width: 60.0,
height: 60.0,
),
const SizedBox(height: 30.0),
RaisedButton(
textColor: Colors.red,
color: Colors.white,
child: Text('Google'),
onPressed: () async{
if(!await user.signInWithGoogle())
showmessage();
},
),
const SizedBox(height: 30.0),
AnimatedSwitcher(
child: signInForm ? LoginForm() : SignupForm(),
duration: Duration(milliseconds: 200),
),
const SizedBox(height: 20.0,),
OutlineButton(
textColor: Colors.white,
child: signInForm ? Text("Sign Up", style: TextStyle(fontWeight: FontWeight.bold, fontSize: 16.0),) : Icon(Icons.arrow_back),
onPressed: () {
setState(() {
signInForm = !signInForm;
});
},
color: Colors.white,
borderSide: BorderSide(color: Colors.white),
highlightColor: Colors.white,
)
],
),
)
),
),
);
}
void showmessage(){
_key.currentState.showSnackBar(SnackBar(
content: Text("Somethimg is wrong"),
));
}
}
class LoginForm extends StatefulWidget {
final Function showError;
const LoginForm({Key key, this.showError}) : super(key: key);
#override
_LoginFormState createState() => _LoginFormState();
}
class _LoginFormState extends State<LoginForm> {
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
final FocusNode passwordField = FocusNode();
TextEditingController _email;
TextEditingController _password;
#override
void initState() {
_email = TextEditingController();
_password = TextEditingController();
super.initState();
}
#override
Widget build(BuildContext context) {
final user = Provider.of<UserRepository>(context);
return Container(
padding: const EdgeInsets.all(16.0),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(10.0),
),
child: Form(
key: _formKey,
child: Column(
children: <Widget>[
Text("Login", style: Theme.of(context).textTheme.display1,),
const SizedBox(height: 20.0),
TextFormField(
controller: _email,
textInputAction: TextInputAction.next,
decoration: InputDecoration(
labelText: "Email"
),
onEditingComplete: (){
FocusScope.of(context).requestFocus(passwordField);
},
),
const SizedBox(height: 16.0,),
TextFormField(
controller: _password,
focusNode: passwordField,
obscureText: true,
decoration: InputDecoration(
labelText: "Password"
),
),
const SizedBox(height: 20.0),
RaisedButton(
textColor: Colors.red,
child: Text("Login"),
onPressed: () async{
if(_formKey.currentState.validate()){
if(!await user.signIn(
_email.text, _password.text))
widget.showError();
}
},
)
],
),
),
);
}
}
class SignupForm extends StatefulWidget {
#override
_SignupFormState createState() => _SignupFormState();
}
class _SignupFormState extends State<SignupForm> {
final FocusNode passwordField = FocusNode();
final FocusNode confirmPasswordField = FocusNode();
TextEditingController _email;
TextEditingController _password;
TextEditingController _confirmpassword;
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
#override
void initState() {
_email = TextEditingController();
_password = TextEditingController();
_confirmpassword = TextEditingController();
super.initState();
}
#override
Widget build(BuildContext context) {
final user = Provider.of<UserRepository>(context);
return Container(
padding: const EdgeInsets.all(16.0),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(10.0),
),
child: Form(
key: _formKey,
child: Column(
children: <Widget>[
Text("Sign Up", style: Theme.of(context).textTheme.display1,),
const SizedBox(height: 20.0),
TextFormField(
controller: _email,
textInputAction: TextInputAction.next,
decoration: InputDecoration(
labelText: "Email"
),
onEditingComplete: (){
FocusScope.of(context).requestFocus(passwordField);
},
),
const SizedBox(height: 16.0,),
TextFormField(
obscureText: true,
controller: _password,
focusNode: passwordField,
decoration: InputDecoration(
labelText: "Password"
),
onEditingComplete: ()=> FocusScope.of(context).requestFocus(confirmPasswordField),
),
const SizedBox(height: 16.0,),
TextFormField(
obscureText: true,
controller: _confirmpassword,
focusNode: confirmPasswordField,
decoration: InputDecoration(
labelText: "Confirm Password"
),
onEditingComplete: (){
FocusScope.of(context).requestFocus(passwordField);
},
),
const SizedBox(height: 20.0),
RaisedButton(
textColor: Colors.red,
child: Text("Create Account"),
onPressed: () async{
if(_formKey.currentState.validate()){
if(_confirmpassword.text == _password.text)
if(!await user.signUp(
_email.text, _password.text))
print("Failed to Signup");
}
},
)
],
),
),
);
}
}
In your code, simply change
builder: (_) => UserRepository.instance(),
to
create: (_) => UserRepository.instance(),
and everything will work just fine!
I am very new to flutter and I had same issue, then I solved with this solution..
I replaced builder with create as shown in image.
for provider 3.2 and up you should use create instead of builder
from the version 5.0.0 of provider package, we should use 'create' instead of 'builder' in ChangeNotifierProvider .
Use create: instead builder: in ChangeNotifiereProvider()
Try with this:
class HomePage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return ChangeNotifierProvider<UserRepository>(
create: (context) => UserRepository.instance(),
child: Consumer
// ignore: missing_return
(builder: (context, UserRepository user, child) {
// ignore: missing_return
switch (user.status){
case Status.Uninitialized:
return Splash();
case Status.Unauthenticated:
case Status.Authenticating:
return LoginPage();
case Status.Authenticated:
return NotesHomePage();
}
}),
);
}
}
I'm a little late but I would like to specify that we can use both create, build and they work too! Maybe I'm missing something but if docs still says that builder can be used I think we should.
By the way this is how you should use it to fix the error :
ChangeNotifierProvider(
create: (_) => UserRepository.instance(),
builder : (newContext,child){ //You will use this context here
return Consumer....
}
child : CircularProgressIndicator(),
Be careful when using contexts, is easy to pass the wrong one. In this case you need to use newContext below.
I used CircularProgressIndicator as child but you can use whatever you prefer.
you can put "BuildContext context, Widget? child"
inside the round brackets(Parameter) of builder

goback in webview flutter by back button device

I have 4 bottomNavigationBar that each one has a webview.
but when tap in a one bottomNavigationBar and select a section in webview page, I can't back to previous page in webview by button back device.
I want back in webview just by back button device.
it is now when the press back button exit the app.
but I use double_back_to_close_app.dart to exit the app.
the home class :
import 'package:webview_flutter/webview_flutter.dart';
import 'package:fancy_bar/fancy_bar.dart';
import 'placeholder_widget.dart';
class Home extends StatefulWidget {
#override
_HomeState createState() => _HomeState();
}
class _HomeState extends State<Home> {
int _currentIndex = 0;
final List<Widget> _children = [
MyPlaceholderWidget('https://googel.com'),
MyPlaceholderWidget('https://googel.com'),
MyPlaceholderWidget('https://googel.com'),
MyPlaceholderWidget('https://googel.com')
];
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: PreferredSize(
preferredSize: Size.fromHeight(40.0),
child: AppBar(
automaticallyImplyLeading: false,
backgroundColor: Colors.grey[200],
elevation: 0.0,
)
),
body: DoubleBackToCloseApp(
child: _children[_currentIndex],
snackBar: const SnackBar(
content: Text(
'برای خروج دو بار کلیک کنید',
style: TextStyle(fontFamily: "Shabnam"),
textDirection: TextDirection.rtl,
textAlign: TextAlign.center,
),
),
),
bottomNavigationBar: FancyBottomBar(
onItemSelected: onTabTapped,
selectedIndex: _currentIndex, // this will be set when a new tab is tapped
items: [
FancyItem(
textColor: Colors.green,
title: 'حساب',
icon: Icon(
LineIcons.user,
size: 29,
),
),
FancyItem(
textColor: Colors.green,
title: 'وبلاگ',
icon: Icon(
LineIcons.file_text,
size: 27,
),
),
FancyItem(
textColor: Colors.green,
title: 'سبد خرید',
icon: Icon(
LineIcons.shopping_cart,
size: 31,
),
),
FancyItem(
textColor: Colors.green,
title: 'صفحه اصلی',
icon: Icon(
LineIcons.home,
size: 28,
),
),
],
),
);
}
void onTabTapped(int index) {
setState(() {
_currentIndex = index;
});
}
}
and the webview class in placeholder_widget.dart:
WebViewController controller;
class MyPlaceholderWidget extends StatelessWidget {
var url = 'https://www.mayehtaj.ir' ;
final key = UniqueKey();
MyPlaceholderWidget(String url){
this.url = url ;
}
#override
Widget build(BuildContext context) {
return WebView(
key: key,
javascriptMode: JavascriptMode.unrestricted,
initialUrl: url,
onWebViewCreated: (WebViewController webViewController){
controller = webViewController;
});
}
}
How to detect press back button device and go back to previous page in one webview ?
thank you for help !
You can copy paste run full code below
You can wrap Scaffold with WillPopScope
When user click device back button, you can execute WebView Controller goback
code snippet onwillpop
#override
Widget build(BuildContext context) {
return WillPopScope(
onWillPop: () => _exitApp(context),
child: Scaffold(
appBar: AppBar(
title: const Text('Flutter WebView example'),
// This drop down menu demonstrates that Flutter widgets can be shown over the web view.
actions: <Widget>[
NavigationControls(_controller.future),
SampleMenu(_controller.future),
],
),
code snippet for exit app
WebViewController controllerGlobal;
Future<bool> _exitApp(BuildContext context) async {
if (await controllerGlobal.canGoBack()) {
print("onwill goback");
controllerGlobal.goBack();
} else {
Scaffold.of(context).showSnackBar(
const SnackBar(content: Text("No back history item")),
);
return Future.value(false);
}
}
working demo
full code
import 'dart:async';
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:webview_flutter/webview_flutter.dart';
void main() => runApp(MaterialApp(home: WebViewExample()));
const String kNavigationExamplePage = '''
<!DOCTYPE html><html>
<head><title>Navigation Delegate Example</title></head>
<body>
<p>
The navigation delegate is set to block navigation to the youtube website.
</p>
<ul>
<ul>https://www.youtube.com/</ul>
<ul>https://www.google.com/</ul>
<ul>https://nodejs.org/en</ul>
</ul>
</body>
</html>
''';
class WebViewExample extends StatefulWidget {
#override
_WebViewExampleState createState() => _WebViewExampleState();
}
WebViewController controllerGlobal;
Future<bool> _exitApp(BuildContext context) async {
if (await controllerGlobal.canGoBack()) {
print("onwill goback");
controllerGlobal.goBack();
} else {
Scaffold.of(context).showSnackBar(
const SnackBar(content: Text("No back history item")),
);
return Future.value(false);
}
}
class _WebViewExampleState extends State<WebViewExample> {
final Completer<WebViewController> _controller =
Completer<WebViewController>();
#override
Widget build(BuildContext context) {
return WillPopScope(
onWillPop: () => _exitApp(context),
child: Scaffold(
appBar: AppBar(
title: const Text('Flutter WebView example'),
// This drop down menu demonstrates that Flutter widgets can be shown over the web view.
actions: <Widget>[
NavigationControls(_controller.future),
SampleMenu(_controller.future),
],
),
// We're using a Builder here so we have a context that is below the Scaffold
// to allow calling Scaffold.of(context) so we can show a snackbar.
body: Builder(builder: (BuildContext context) {
return WebView(
initialUrl: 'https://flutter.dev',
javascriptMode: JavascriptMode.unrestricted,
onWebViewCreated: (WebViewController webViewController) {
_controller.complete(webViewController);
},
// TODO(iskakaushik): Remove this when collection literals makes it to stable.
// ignore: prefer_collection_literals
javascriptChannels: <JavascriptChannel>[
_toasterJavascriptChannel(context),
].toSet(),
navigationDelegate: (NavigationRequest request) {
if (request.url.startsWith('https://www.youtube.com/')) {
print('blocking navigation to $request}');
return NavigationDecision.prevent;
}
if (request.url.startsWith('https://flutter.dev/docs')) {
print('blocking navigation to $request}');
return NavigationDecision.prevent;
}
print('allowing navigation to $request');
return NavigationDecision.navigate;
},
onPageFinished: (String url) {
print('Page finished loading: $url');
},
);
}),
floatingActionButton: favoriteButton(),
),
);
}
JavascriptChannel _toasterJavascriptChannel(BuildContext context) {
return JavascriptChannel(
name: 'Toaster',
onMessageReceived: (JavascriptMessage message) {
Scaffold.of(context).showSnackBar(
SnackBar(content: Text(message.message)),
);
});
}
Widget favoriteButton() {
return FutureBuilder<WebViewController>(
future: _controller.future,
builder: (BuildContext context,
AsyncSnapshot<WebViewController> controller) {
if (controller.hasData) {
return FloatingActionButton(
onPressed: () async {
final String url = await controller.data.currentUrl();
Scaffold.of(context).showSnackBar(
SnackBar(content: Text('Favorited $url')),
);
},
child: const Icon(Icons.favorite),
);
}
return Container();
});
}
}
enum MenuOptions {
showUserAgent,
listCookies,
clearCookies,
addToCache,
listCache,
clearCache,
navigationDelegate,
}
class SampleMenu extends StatelessWidget {
SampleMenu(this.controller);
final Future<WebViewController> controller;
final CookieManager cookieManager = CookieManager();
#override
Widget build(BuildContext context) {
return FutureBuilder<WebViewController>(
future: controller,
builder:
(BuildContext context, AsyncSnapshot<WebViewController> controller) {
return PopupMenuButton<MenuOptions>(
onSelected: (MenuOptions value) {
switch (value) {
case MenuOptions.showUserAgent:
_onShowUserAgent(controller.data, context);
break;
case MenuOptions.listCookies:
_onListCookies(controller.data, context);
break;
case MenuOptions.clearCookies:
_onClearCookies(context);
break;
case MenuOptions.addToCache:
_onAddToCache(controller.data, context);
break;
case MenuOptions.listCache:
_onListCache(controller.data, context);
break;
case MenuOptions.clearCache:
_onClearCache(controller.data, context);
break;
case MenuOptions.navigationDelegate:
_onNavigationDelegateExample(controller.data, context);
break;
}
},
itemBuilder: (BuildContext context) => <PopupMenuItem<MenuOptions>>[
PopupMenuItem<MenuOptions>(
value: MenuOptions.showUserAgent,
child: const Text('Show user agent'),
enabled: controller.hasData,
),
const PopupMenuItem<MenuOptions>(
value: MenuOptions.listCookies,
child: Text('List cookies'),
),
const PopupMenuItem<MenuOptions>(
value: MenuOptions.clearCookies,
child: Text('Clear cookies'),
),
const PopupMenuItem<MenuOptions>(
value: MenuOptions.addToCache,
child: Text('Add to cache'),
),
const PopupMenuItem<MenuOptions>(
value: MenuOptions.listCache,
child: Text('List cache'),
),
const PopupMenuItem<MenuOptions>(
value: MenuOptions.clearCache,
child: Text('Clear cache'),
),
const PopupMenuItem<MenuOptions>(
value: MenuOptions.navigationDelegate,
child: Text('Navigation Delegate example'),
),
],
);
},
);
}
void _onShowUserAgent(
WebViewController controller, BuildContext context) async {
// Send a message with the user agent string to the Toaster JavaScript channel we registered
// with the WebView.
controller.evaluateJavascript(
'Toaster.postMessage("User Agent: " + navigator.userAgent);');
}
void _onListCookies(
WebViewController controller, BuildContext context) async {
final String cookies =
await controller.evaluateJavascript('document.cookie');
Scaffold.of(context).showSnackBar(SnackBar(
content: Column(
mainAxisAlignment: MainAxisAlignment.end,
mainAxisSize: MainAxisSize.min,
children: <Widget>[
const Text('Cookies:'),
_getCookieList(cookies),
],
),
));
}
void _onAddToCache(WebViewController controller, BuildContext context) async {
await controller.evaluateJavascript(
'caches.open("test_caches_entry"); localStorage["test_localStorage"] = "dummy_entry";');
Scaffold.of(context).showSnackBar(const SnackBar(
content: Text('Added a test entry to cache.'),
));
}
void _onListCache(WebViewController controller, BuildContext context) async {
await controller.evaluateJavascript('caches.keys()'
'.then((cacheKeys) => JSON.stringify({"cacheKeys" : cacheKeys, "localStorage" : localStorage}))'
'.then((caches) => Toaster.postMessage(caches))');
}
void _onClearCache(WebViewController controller, BuildContext context) async {
await controller.clearCache();
Scaffold.of(context).showSnackBar(const SnackBar(
content: Text("Cache cleared."),
));
}
void _onClearCookies(BuildContext context) async {
final bool hadCookies = await cookieManager.clearCookies();
String message = 'There were cookies. Now, they are gone!';
if (!hadCookies) {
message = 'There are no cookies.';
}
Scaffold.of(context).showSnackBar(SnackBar(
content: Text(message),
));
}
void _onNavigationDelegateExample(
WebViewController controller, BuildContext context) async {
final String contentBase64 =
base64Encode(const Utf8Encoder().convert(kNavigationExamplePage));
controller.loadUrl('data:text/html;base64,$contentBase64');
}
Widget _getCookieList(String cookies) {
if (cookies == null || cookies == '""') {
return Container();
}
final List<String> cookieList = cookies.split(';');
final Iterable<Text> cookieWidgets =
cookieList.map((String cookie) => Text(cookie));
return Column(
mainAxisAlignment: MainAxisAlignment.end,
mainAxisSize: MainAxisSize.min,
children: cookieWidgets.toList(),
);
}
}
class NavigationControls extends StatelessWidget {
const NavigationControls(this._webViewControllerFuture)
: assert(_webViewControllerFuture != null);
final Future<WebViewController> _webViewControllerFuture;
#override
Widget build(BuildContext context) {
return FutureBuilder<WebViewController>(
future: _webViewControllerFuture,
builder:
(BuildContext context, AsyncSnapshot<WebViewController> snapshot) {
final bool webViewReady =
snapshot.connectionState == ConnectionState.done;
final WebViewController controller = snapshot.data;
controllerGlobal = controller;
return Row(
children: <Widget>[
IconButton(
icon: const Icon(Icons.arrow_back_ios),
onPressed: !webViewReady
? null
: () async {
if (await controller.canGoBack()) {
controller.goBack();
} else {
Scaffold.of(context).showSnackBar(
const SnackBar(content: Text("No back history item")),
);
return;
}
},
),
IconButton(
icon: const Icon(Icons.arrow_forward_ios),
onPressed: !webViewReady
? null
: () async {
if (await controller.canGoForward()) {
controller.goForward();
} else {
Scaffold.of(context).showSnackBar(
const SnackBar(
content: Text("No forward history item")),
);
return;
}
},
),
IconButton(
icon: const Icon(Icons.replay),
onPressed: !webViewReady
? null
: () {
controller.reload();
},
),
],
);
},
);
}
}

Categories

Resources