The provider in flutter doesn't change a screens - flutter

I'm using a provider in flutter app to change screens in my app. Here is my main.dart file:
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:school_app/services/auth_service.dart';
import 'package:school_app/wrapper.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return ChangeNotifierProvider(
create: (_) => AuthService().auth,
child: MaterialApp(
home: Wrapper(),
),
);
}
}
Also this is my wrapper.dart file where the screens choose:
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:school_app/screens/authenticate/auth.dart';
import 'package:school_app/models/user.dart';
import 'package:school_app/screens/school/home.dart';
import 'package:school_app/services/auth_service.dart';
class Wrapper extends StatelessWidget {
#override
Widget build(BuildContext context) {
final user = Provider.of<AuthProvider>(context);
print(user.auth);
if(!user.auth) return Auth();
return Home();
}
}
And it is my AuthProvider class:
import 'package:dio/dio.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:shared_preferences/shared_preferences.dart';
class AuthService {
/* AuthUser _user(User user) {
return user != null ? AuthUser(uid: user.uid) : null;
}*/
AuthProvider auth = new AuthProvider();
//sign in
Future signIn(String username, String password) async {
try {
SharedPreferences prefs = await SharedPreferences.getInstance();
var dio = Dio();
Response user = await dio.post('url', data: {
'username': username,
'password': password
});
if(user.data['success'] == false) return user.data['msg'];
await prefs.setString('token', user.data['token']);
auth.setAuth(true);
print("SUCCESS");
} catch(e) {
print('Error ' + e.toString());
}
}
}
class AuthProvider with ChangeNotifier {
bool _auth;
AuthProvider() {
_auth = false;
}
bool get auth => _auth;
void setAuth(bool auth) {
_auth = auth;
notifyListeners();
}
}
Also I made this variants of Wrapper widget, but it didn't work also:
class Wrapper extends StatelessWidget {
#override
Widget build(BuildContext context) {
final user = context.watch<AuthProvider>();
print(user.auth);
if(!user.auth) return Auth();
return Home();
}
}
And this one:
class Wrapper extends StatelessWidget {
#override
Widget build(BuildContext context) {
final user = context.watch<AuthProvider>();
print(user.auth);
if(!user.auth) return Auth();
return Home();
}
}
And when I call a function in AuthProvider class setAuth, nothing changed. Can you help me and find my mistake?

Related

Get FCM registration token in Flutter

Hi all...
I have problem in getting token value
my code is
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/material.dart';
import 'package:firebase_messaging/firebase_messaging.dart';
Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage
message) async {
await Firebase.initializeApp();
print('A bg message just showed up : ${message.messageId}');
}
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);
runApp(Application());
}
class Application extends StatefulWidget {
#override
State<StatefulWidget> createState() => _Application();
}
class _Application extends State<Application> {
final FirebaseMessaging _firebaseMessaging = FirebaseMessaging.instance;
String token = '';
#override
void initState() {
super.initState();
_firebaseMessaging.getToken().then((token) {
assert(token != null);
print("token is : " + token!);
});
}
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: Center(child: Text('My Token is : $token')),),
);
}
}

Flutter: How to store the location data?

Errors:
The named parameter 'locationData' is required, but there's no
corresponding argument. Try adding the required argument.
Undefined name 'locationData'. Try correcting the name to one that is defined, or defining the name.
The location data in the main.dart file bring an error please help me solve it.
My code:
Below is the main.dart code that caused the error:
import 'package:firebase_core/firebase_core.dart';
import 'package:firstapp/screens/Login_screen.dart';
import 'package:firstapp/screens/authunication/phone_auth_screen.dart';
import 'package:firstapp/screens/home_screen.dart';
import 'package:firstapp/screens/location_screen.dart';
import 'package:firstapp/screens/splash_screen.dart';
import 'package:flutter/material.dart';
import 'package:location/location.dart';
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp(MyApp());
}
// ignore: use_key_in_widget_constructors
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
theme: ThemeData(
primaryColor: Colors.cyan.shade900,
),
initialRoute: SplashScreen.id,
routes: {
SplashScreen.id: (context) => SplashScreen(),
LoginScreen.id: (context) => LoginScreen(),
PhoneAuthScreen.id: (context) => PhoneAuthScreen(),
LocationScreen.id: (context) => LocationScreen(),
HomeScreen.id: (context) => HomeScreen(locationData),
},
);`
Below is the code for the HomeScreen widget where I want location data:
import 'package:flutter/material.dart';
import 'package:location/location.dart';
class HomeScreen extends StatelessWidget {
static const String id = 'home-screen';
final LocationData locationData;
HomeScreen({
required this.locationData,
});
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(locationData.latitude.toString()),
),
body: Center(
child: Text('Home screen'),
),
);
}
}
Here is the code for the LocationScreen widget in the app:
import 'package:firebase_auth/firebase_auth.dart';
import 'package:firstapp/screens/login_screen.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:location/location.dart';
class LocationScreen extends StatefulWidget {
static const String id = 'location-screen';
#override
State<LocationScreen> createState() => _LocationScreenState();
}
class _LocationScreenState extends State<LocationScreen> {
Location location = new Location();
late bool _serviceEnabled;
late PermissionStatus _permissionGranted;
late LocationData _locationData;
Future<LocationData?>getLocation() async {
_serviceEnabled = await location.serviceEnabled();
if (!_serviceEnabled) {
_serviceEnabled = await location.requestService();
if (!_serviceEnabled) {
return null;
}
}
_permissionGranted = await location.hasPermission();
if (_permissionGranted == PermissionStatus.denied) {
_permissionGranted = await location.requestPermission();
if (_permissionGranted != PermissionStatus.granted) {
return null;
}
}
_locationData = await location.getLocation();
return _locationData;
}
So,
The first error says that when you try to implement a HomeScreen widget, withoug giving the required parameter named locationData,to give the required parameted follow this code example:
HomeScreen(locationData: locationData)
The second error says that you are trying to use a variable that is not defined yet. In the page that you try to implement the HomeScreen widget, you should define a variable named locationData, to give it a value and then to try to use the HomeScreen widget. For instance:
class LocationScreen extends StatefulWidget {
const LocationScreen({Key? key}) : super(key: key);
#override
State<LocationScreen> createState() => _LocationScreenState();
}
class _LocationScreenState extends State<LocationScreen> {
LocationData locationData;
#override
Widget build(BuildContext context) {
//in case that you use the HomeScreen as a widget
return locationData == null ? Container() : HomeScreen(locationData: locationData);
//in case that you use the HomeScreen as Screen
return Container();
}
void getLocationData() async{
//in case that you use the HomeScreen as a widget
//this function will change the state after the locationData have been filled
locationData = await getLocation();
setState(() {
});
}
void navigateToHomeScreen() async{
//in case that you use the HomeScreen as a widget
//this function will change the state after the locationData have been filled
locationData = await getLocation();
Navigator.push(context, MaterialPageRoute(
builder: (context) => HomeScreen(locationData: locationData);
));
}
Future<LocationData?> getLocation() async {
_serviceEnabled = await location.serviceEnabled();
if (!_serviceEnabled) {
_serviceEnabled = await location.requestService();
if (!_serviceEnabled) {
return null;
}
}
_permissionGranted = await location.hasPermission();
if (_permissionGranted == PermissionStatus.denied) {
_permissionGranted = await location.requestPermission();
if (_permissionGranted != PermissionStatus.granted) {
return null;
}
}
_locationData = await location.getLocation();
return _locationData;
}
}
The third error says that don't use any Positional parameters in your HomeScreen class because you use named parameters, If you follow my code, it will be fixed as well
Hope I helped,
Enjoy flutter
On the HomeScreen, replace this line of code:
HomeScreen({required this.locationData,});
with
HomeScreen(this.locationData);
This should clear the error on main.dart

How to check if user is SignedIn in Flutter Firebase

Hy here everyone. I am new to flutter and i want to check if User is SignedIn. If so the user navigate to HomeScreen else SplashScreen.
Here is my main.dart
void main() async{
runApp(MyApp());
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Instant Tasker',
theme: theme(),
initialRoute: SplashScreen.routeName,
routes: routes,
);
}
}
Here is Splash Screen
class SplashScreen extends StatefulWidget {
static String routeName = "/splash";
#override
_SplashScreenState createState() => _SplashScreenState();
}
class _SplashScreenState extends State<SplashScreen> {
startTime() async {
var _duration = new Duration(seconds: 2);
return new Timer(_duration, navigationPage);
}
void navigationPage() {
var auth = FirebaseAuth.instance;
// ignore: deprecated_member_use
auth.onAuthStateChanged.listen((user) {
if (user != null) {
Navigator.of(context).pushAndRemoveUntil(
MaterialPageRoute(builder: (context) => MainScreen()),
(Route<dynamic> route) => false);
} else {}
});
}
#override
void initState() {
super.initState();
startTime();
}
#override
Widget build(BuildContext context) {
SizeConfig().init(context);
return Scaffold(
body: Body()
);
}
}
However i achieved to check user at splash screen but it stays at splash screen to check user then move to HomeScreen which doesn't seems to be good.
Or can anybody suggest how to show CircularProgressIndicator instead of Splash Screen body when it is checking for user
You can achieve it using StreamProvder
Implementation
Steps
Create a CustomUser Data model.
class CustomUser {
final String userId;
CustomUser({this.userId});
}
Create a class named FirebaseAuthService and create a stream to listen to Firebase AuthStateChanges
import 'package:firebase_auth/firebase_auth.dart';
class FirebaseAuthService {
final FirebaseAuth auth = FirebaseAuth.instance;
// create user obj based on firebase user
CustomUser _userFromFirebaseUser(User user) {
return user != null ? CustomUser(userId: user.uid) : null;
}
// auth change user stream
//Required stream
Stream<CustomUser> get user {
return auth.authStateChanges().map(_userFromFirebaseUser);
}
}
}
Add a StreamProvider on top of the widget tree where you want to check for the AuthState.
void main() async{
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return StreamProvider<CustomUser>.value(
value: FirebaseAuthService().user,
child: MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Instant Tasker',
theme: theme(),
initialRoute: SplashScreen.routeName,
routes: routes,
)
);
}
}
Create a Wrapper and return SplashScreen or HomeScreen based on AuthState.
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
class Wrapper extends StatefulWidget {
#override
_WrapperState createState() => _WrapperState();
}
class _WrapperState extends State<Wrapper> {
#override
Widget build(BuildContext context) {
final user = Provider.of<CustomUser>(context);
if (user == null) {
return SplashScreen();
}
return HomeScreen();
}
}
Now you can use final user = Provider.of<CustomUser>(context);
in the widget tree to check if the user is null.
https://www.youtube.com/watch?v=z05m8nlPRxk&list=PL4cUxeGkcC9j--TKIdkb3ISfRbJeJYQwC&index=3

a problem when using shared preference's plugin

I'm working on flutter and I've a problem with Shared preference plugin
this is my code:
import 'package:flutter/material.dart';
import 'package:newsapp/screens/homescreen.dart';
import 'package:newsapp/screens/onboarding.dart';
import 'package:shared_preferences/shared_preferences.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
SharedPreferences prefs = await SharedPreferences.getInstance();
int seen1 = prefs.getInt('seen');
Widget _screen = HomeScreen();
if (seen1 == null || seen1 == 0)
_screen = OnBoarding();
else {
_screen = HomeScreen();
}
}
class NewsApp extends StatelessWidget {
final Widget _screen;
NewsApp(this._screen);
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: this._screen,
);
}
}
this is the error messeage:
No preferred FlutterEngine was provided. Creating a new FlutterEngine for this FlutterFragment.
D/FlutterActivityAndFragmentDelegate( 1817): Attaching FlutterEngine to the Activity that owns this Fragment.
D/FlutterView( 1817): Attaching to a FlutterEngine: io.flutter.embedding.engine.FlutterEngine#e2b1eab
We need to add WidgetsFlutterBinding.ensureInitialized(); before getting
await SharedPreferences.getInstance();
Like :
void main() async {
WidgetsFlutterBinding.ensureInitialized();
SharedPreferences prefs = await SharedPreferences.getInstance();
int seen1 = prefs.getInt('seen');
Widget _screen = HomeScreen();
if (seen1 == null || seen1 == 0)
_screen = OnBoarding();
else {
_screen = HomeScreen();
}
}
you have to end your main() method with runApp(NewsApp());
move your logic code from main() method to NewsApp widget.
convert your NewsApp widget to StatefulWidget to fitch your data from SharedPreferences.
like this :
import 'package:flutter/material.dart';
import 'package:newsapp/screens/homescreen.dart';
import 'package:newsapp/screens/onboarding.dart';
import 'package:shared_preferences/shared_preferences.dart';
void main() {
runApp(NewsApp());
}
class NewsApp extends StatefulWidget {
#override
_NewsAppState createState() => _NewsAppState();
}
class _NewsAppState extends State<NewsApp> {
final Widget _screen = HomeScreen();
#override
void initState() {
super.initState();
Future.delayed(Duration.zero, () async {
SharedPreferences prefs = await SharedPreferences.getInstance();
int seen1 = prefs.getInt('seen');
if (seen1 == null || seen1 == 0)
_screen = OnBoarding();
else {
_screen = HomeScreen();
}
setState(() {});
}
}
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: this._screen,
);
}
}

How to pass user to named route with a stateful widget using Navigator in flutter?

My Setup
Login Route - asynchronously checks if a user is logged
Checkin Route - Consists of a main stateful widget and several stateless children widgets that depend on the information of the logged in user
My Idea
After the user login is identified on the login route, use Navigator to pass along the user object to the checkin route
children widgets can receive the user object through their constructor
My Problem
the user object is not 'arriving' in the stateful widget of the checkin route
Files
Here are the relevant code snippets to check (I added them from more complex files and hope I got all imports correct):
Main.dart
import 'package:flutter/material.dart';
import 'loginView.dart';
import 'checkinView.dart';
void main() {
WidgetsFlutterBinding.ensureInitialized();
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
initialRoute: '/',
routes: <String, WidgetBuilder>{
'/': (BuildContext context) => LoginView(),
'/checkin': (BuildContext context) => CheckinView(),
}
);
}
}
LoginView.dart
import 'package:flutter/material.dart';
import 'package:firebase_auth/firebase_auth.dart';
class LoginView extends StatefulWidget {
#override
LoginViewState createState() => LoginViewState();
}
class LoginViewState extends State<LoginView> {
#override
void initState() {
super.initState();
checkLoginAndRedirect();
}
void checkLoginAndRedirect() async {
FirebaseAuth.instance.currentUser().then((FirebaseUser currUser){
if(currUser != null){
print("[LoginView - user] $currUser");
Navigator.pushNamed(context, '/checkin', arguments: {currUser});
}
});
}
}
CheckinView.dart
import 'package:flutter/material.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'checkinList.dart';
class CheckinView extends StatefulWidget {
final FirebaseUser user;
CheckinView({this.user});
#override
CheckinViewState createState() {
print("[CheckinView - user] $user");
return CheckinViewState();
}
}
class CheckinViewState extends State<CheckinView> {
FirebaseUser _currentUser;
#override
void initState() {
super.initState();
setState((){
_currentUser = widget.user;
});
}
#override
Widget build(BuildContext context) {
print("[CheckinViewState - widget.user] " + widget.user.toString());
return Scaffold(
body: new Column(
children: <Widget>[
CheckinList(user: _currentUser)
]
)
);
}
}
CheckinList.dart
import 'package:flutter/material.dart';
import 'package:firebase_auth/firebase_auth.dart';
class CheckinList extends StatelessWidget{
FirebaseUser user = null;
CheckinList({this.user = null});
#override
Widget build(BuildContext context) {
return Container(
padding: EdgeInsets.all(30.0),
child: Text(user['name'])
);
}
}
Solution
The arguments need extraction in the build method of the WidgetState (because only there we have the BuildContext)
Navigator is automagically handling the arguments correct, even with Stateful Widgets - THANKS FLUTTER TEAM!!!!
My adapted Files
Main.dart
import 'package:flutter/material.dart';
import 'loginView.dart';
import 'checkinView.dart';
void main() {
WidgetsFlutterBinding.ensureInitialized();
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
initialRoute: '/',
routes: <String, WidgetBuilder>{
'/': (BuildContext context) => LoginView(),
'/checkin': (BuildContext context) => CheckinView(),
}
);
}
}
LoginView.dart
import 'package:flutter/material.dart';
import 'package:firebase_auth/firebase_auth.dart';
class LoginView extends StatefulWidget {
#override
LoginViewState createState() => LoginViewState();
}
class LoginViewState extends State<LoginView> {
#override
void initState() {
super.initState();
checkLoginAndRedirect();
}
void checkLoginAndRedirect() async {
FirebaseAuth.instance.currentUser().then((FirebaseUser currUser){
if(currUser != null){
print("[LoginView - user] $currUser");
//// pass a map as 'arguments' to be ready to pass along more variables in the future
Navigator.pushNamed(context, '/checkin', arguments: {'user': currUser});
}
});
}
}
CheckinView.dart
import 'package:flutter/material.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'checkinList.dart';
class CheckinView extends StatefulWidget {
//// leave the stateful class as usual
#override
CheckinViewState createState() => CheckinViewState();
}
class CheckinViewState extends State<CheckinView> {
//// don't bother in the initState method as the BuildContext is missing here
#override
Widget build(BuildContext context) {
//// access the passed arguments
Map<String, dynamic> args = ModalRoute.of(context).settings.arguments;
print("[CheckinViewState - widget.user] " + widget.user.toString());
return Scaffold(
body: new Column(
children: <Widget>[
//// access the passed variables through 'args'
CheckinList(user: args.user)
]
)
);
}
}
CheckinList.dart
import 'package:flutter/material.dart';
import 'package:firebase_auth/firebase_auth.dart';
class CheckinList extends StatelessWidget{
FirebaseUser user = null;
//// assign the user in your constructor
CheckinList(FirebaseUser user){
this.user = user;
};
#override
Widget build(BuildContext context) {
return Container(
padding: EdgeInsets.all(30.0),
child: Text(user['name'])
);
}
}
Read more in the cookbook about it:
https://flutter.dev/docs/cookbook/navigation/passing-data#create-a-detail-screen-to-extract-the-arguments
https://flutter.dev/docs/cookbook/navigation/navigate-with-arguments
You can try it. I hope it can help you
class LoginViewState extends State<LoginView> {
#override
void initState() {
super.initState();
checkLoginAndRedirect();
}
void checkLoginAndRedirect() async {
FirebaseAuth.instance.currentUser().then((FirebaseUser currUser){
if(currUser != null){
print("[LoginView - user] $currUser");
Navigator.pushNamed(context, '/checkin', arguments: currUser); /// <--- fixed here
}
});
}
}
class CheckinView extends StatefulWidget {
RouteSettings settings = ModalRoute.of(context).settings /// <--- fixed here
FirebaseUser user = settings.arguments; /// <--- fixed here
#override
CheckinViewState createState() {
print("[CheckinView - user] $user");
return CheckinViewState();
}
}
You need to access passed data using ModalRoute inside build method, below code will give you can idea how to do it,
class LevelViewChildScreen extends StatelessWidget {
UserModel user;
#override
Widget build(BuildContext context) {
user = ModalRoute.of(context).settings.arguments;
return LevelViewChild(user);
}
}
class LevelViewChild extends StatefulWidget {
UserModel user;
LevelViewChild(this.user);