handle link and navigate with firebase dynamic links in flutter - firebase

I am using firebase_dynamic_links 0.5.0 in my flutter project app. I am using bloc for state management and I have the issue with the navigation. I am handling the link normally but when I try to navigate to another page that's not work . I don't know why ?
PS : I have test the firebase_dynamic_links example and that's work normally
main() {
WidgetsFlutterBinding.ensureInitialized();
// Production code
ErrorWidget.builder = (FlutterErrorDetails details) => Container();
runApp(MyApp());
}
// Myapp class here I get the result of print but navigation don't work
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
final _authBloc = AuthBloc();
final _progressBloc = new ProgressBloc();
#override
void initState() {
super.initState();
initDynamicLinks();
}
void initDynamicLinks() async {
final PendingDynamicLinkData data =
await FirebaseDynamicLinks.instance.getInitialLink();
final Uri deepLink = data?.link;
//print("hhhhhhh" + deepLink.path);
if (deepLink != null) {
print("Link :" + deepLink.path);
// this line don't work
Navigator.pushNamed(context, '/helloworld');
}
FirebaseDynamicLinks.instance.onLink(
onSuccess: (PendingDynamicLinkData dynamicLink) async {
final Uri deepLink = dynamicLink?.link;
if (deepLink != null) {
print("Link :" + deepLink.path);
Navigator.pushNamed(context, '/helloworld');
}
}, onError: (OnLinkErrorException e) async {
print('onLinkError');
print(e.message);
});
}
#override
Widget build(BuildContext context) {
ThemeData themeData = ThemeData(
fontFamily: Constants.DEFAULT_FONT_NAME,
textTheme: Theme.of(context)
.textTheme
.apply(fontFamily: Constants.DEFAULT_FONT_NAME),
brightness: Brightness.light,
canvasColor: Colors.black12,
scaffoldBackgroundColor: Colors.white,
primaryIconTheme:
Theme.of(context).primaryIconTheme.copyWith(color: Colors.white),
accentIconTheme:
Theme.of(context).accentIconTheme.copyWith(color: Colors.white),
primaryTextTheme:
Theme.of(context).primaryTextTheme.apply(bodyColor: Colors.white),
primaryColorLight: Colors.white,
primaryColorBrightness: Brightness.light,
primarySwatch: Constants.COLOR_PRIMARY,
primaryColor: Constants.COLOR_PRIMARY,
);
var home = Stack(children: <Widget>[
RootPage(),
ProgressDialog(),
]);
return ProgressProvider(
dialog: _progressBloc,
child: AuthBlocProvider(
bloc: _authBloc,
child: StoreBlocProvider(
store: Store(),
child: RootProvider(
bloc: new RootBloc(),
child: MaterialApp(
localizationsDelegates: [
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
],
supportedLocales: [
const Locale('en'), // English
const Locale('fr'), // Hebrew
],
title: 'Foodynasty',
theme: themeData,
debugShowCheckedModeBanner: false,
routes: <String, WidgetBuilder>{
'/': (BuildContext context) => home,
'/helloworld': (BuildContext context) => RestaurantShare(
restaurantId: 'test',
),
},
),
),
),
),
);
}
}
thank you, and sorry for my bad English.

It could be that the deepLink is null and the code inside the if statement is not executed. Could you provide an example of the deep link you are sending to your app? It could be that firebase_dynamic_links cannot parse the deep link correctly if it has an error.

Related

Flutter streamprovider issue with userdata from Firebase

I've been stuck in this issue for sometime now and after lots of tries, now i don't know where the issue is.
The problem is i'm using StreamProvider to get userData from firebase and then use that userData through provider throughout my project, Now everything works fine but the issue arises when i try to log out. Actually even logout works perfectly, untill i close the app using the backbutton of my phone and then reopen the app from open apps list, then when i try to logout it doesn't work correctly. When i refresh the app the user is logged out but initially it doesn't navigate to the login screen.
here is the logout code.
InkWell(
onTap: () async {
await showDialog(
context: context,
useRootNavigator: false,
builder: (ctx) => AlertDialog(
title: const Text("Are you Sure?"),
content: const Text("Do You want to logout?"),
actions: [
TextButton(
onPressed: () {
Navigator.of(context).pop();
},
child: const Text(
"No",
style: TextStyle(
color: ColorConstant.appColor,
),
),
),
TextButton(
onPressed: () async {
Navigator.of(context).pop(true);
await AuthService().signOut();
Navigator.of(context).pushAndRemoveUntil(
MaterialPageRoute(
builder: ((context) =>
const LoginScreen())),
(route) => false);
},
child: const Text(
"Yes",
style: TextStyle(
color: ColorConstant.appColor,
),
),
),
],
),
);
},
And here is the main.dart i've defined StreamProvider.
class MyApp extends StatefulWidget {
const MyApp({Key? key}) : super(key: key);
#override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
#override
Widget build(BuildContext context) {
final _auth = AuthService();
final String? _userId = _auth.user?.uid;
return MultiProvider(
providers: [
StreamProvider<List<Packages>>(
create: (_) => DatabaseService().packages, initialData: const []),
StreamProvider<UserModel?>(
create: (_) => DatabaseService().userData(_userId),
initialData: null,
),
],
builder: (context, child) {
return MaterialApp(
title: 'DreamaX',
debugShowCheckedModeBanner: false,
theme: ThemeData(
primaryColor: ColorConstant.appColor,
errorColor: Colors.red,
),
home: const Wrapper(),
);
},
);
}
}
Also one another thing that when i'm logging in, i navigate to MyApp so that the StreamProvider can get the userId and initialize the streamprovider.
here is the login code
try {
await _authService.signIn(
emailCont: _emailCont.text.trim(),
passCont: _passCont.text.trim());
Navigator.of(context)
.pushAndRemoveUntil(
MaterialPageRoute(
builder: ((context) =>
const MyApp())),
(route) => false);
} on FirebaseAuthException catch (e) {
So i'm really confused now what could be the cause here?

Provider in different route - Flutter

I'm new in Flutter development, i'm making an app with Firebase Auth, in where I'm using an Authentication Wrapper class that, if user is logged in, goes to Home Screen, else goes to SignIn Screen.
The problem is that, when I want to navigate to AuthWrapper, I get this error message in a red screen:
Error: Could not find the correct Provider<UserFirebaseModel> above this Builder Widget
This happens because you used a `BuildContext` that does not include the provider
of your choice. There are a few common scenarios:
- You added a new provider in your `main.dart` and performed a hot-reload.
To fix, perform a hot-restart.
- The provider you are trying to read is in a different route.
Providers are "scoped". So if you insert of provider inside a route, then
other routes will not be able to access that provider.
- You used a `BuildContext` that is an ancestor of the provider you are trying to read.
Make sure that Builder is under your MultiProvider/Provider<UserFirebaseModel>.
This usually happens when you are creating a provider and trying to read it immediately.
Here there are the most important classes of my code, where I think the problem is.
main.dart
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return StreamProvider.value(
value: Authentication().user,
initialData: UserFirebaseModel.initialData(),
child: MaterialApp(
title: 'AccessCity',
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: Colors.blue,
fontFamily: 'Montserrat',
),
routes: getAppRoutes(),
initialRoute: '/',
onGenerateRoute: (RouteSettings settings) {
return MaterialPageRoute(
builder: (BuildContext context) => HomeTempPage(),
);
},
),
);
}
}
authWrapper.dart
class AuthWrapper extends StatelessWidget {
#override
Widget build(BuildContext context) {
final user = Provider.of<UserFirebaseModel>(context);
print(user);
// ignore: unnecessary_null_comparison
if (user == null) {
return LoginPage();
} else {
return Home();
}
}
}
routes.dart
Map<String, WidgetBuilder> getAppRoutes() {
return <String, WidgetBuilder>{
// Home Temporal Page
'/': (BuildContext context) => HomeTempPage(),
// Components Pages
'generalBigButton': (BuildContext context) => GeneralBigButtonPage(),
'textEntryField': (BuildContext context) => TextEntryFieldPage(),
'secureTextEntryField': (BuildContext context) =>
SecureTextEntryFieldPage(),
'underlinedButton': (BuildContext context) => UnderlinedButtonPage(),
// Modules
'login': (BuildContext context) => LoginPage(),
'authWrapper': (BuildContext context) => AuthWrapper(),
};
}
homeTempPage.dart
const String _title = 'Home Temporal';
const String _goAccessCityButton = 'Ir a AccessCity';
const String _goAccessCityLabel = 'Ir a pantalla Login de la app';
class HomeTempPage extends StatefulWidget {
#override
_HomeTempPageState createState() => _HomeTempPageState();
}
class _HomeTempPageState extends State<HomeTempPage> {
final model = HomeTempModel();
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(_title),
backgroundColor: mainBlue,
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: [
SizedBox(height: 10),
MaterialButton(
child: Text(_goAccessCityButton),
color: Colors.blue,
textColor: Colors.white,
onPressed: () {
model.navigateToStart(context);
},
),
Text(_goAccessCityLabel),
SizedBox(height: 30),
Expanded(
child: Container(
child: ListView(
children: model.getComponents(context),
),
),
),
],
),
),
);
}
}
In this last class, the line
navigateToStart() method
goes to route 'authWrapper'.
UserFirebaseModel
class UserFirebaseModel {
final String id;
final String email;
UserFirebaseModel(this.id, this.email);
factory UserFirebaseModel.initialData() {
return UserFirebaseModel('', '');
}
}
Authentication class
class Authentication {
final FirebaseAuth _firebaseAuth = FirebaseAuth.instance;
// Create user based on User (ex FirebaseUser)
UserFirebaseModel? _userFromFirebaseUser(User? user) {
final _mail;
if (user != null) {
_mail = user.email;
if (_mail != null) {
return UserFirebaseModel(user.uid, _mail);
}
} else {
return null;
}
}
// Auth change user stream
Stream<UserFirebaseModel?> get user {
return _firebaseAuth.authStateChanges().map(_userFromFirebaseUser);
}
// Sign in with email and password
Future<String?> signIn({
required String email,
required String password,
}) async {
try {
await _firebaseAuth.signInWithEmailAndPassword(
email: email,
password: password,
);
print("Signed in");
return "";
} on FirebaseAuthException catch (e) {
return e.message;
}
}
User? getUser() {
try {
return _firebaseAuth.currentUser;
} on FirebaseAuthException {
return null;
}
}
}
Thanks for your help, i need to solve it!

FirebaseAuth.instance.userChanges stream does not emit the signed in user after page reload (web)

On Flutter Web, the userChanges stream from a FirebaseAuth instance never emits the signed in user after a page reload. Instead, it only emits null. With the example below, the app gets stuck on the loading page.
import 'package:firebase_auth/firebase_auth.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
void main() {
WidgetsFlutterBinding.ensureInitialized();
runApp(App());
}
class App extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
initialRoute: '/loading',
onGenerateRoute: (settings) {
switch (settings.name) {
case '/error':
return MaterialPageRoute(builder: (_) => ErrorScreen());
case '/loading':
return MaterialPageRoute(builder: (_) => LoadingScreen());
case '/signin':
return MaterialPageRoute(builder: (_) => SignInScreen());
case '/welcome':
return MaterialPageRoute(builder: (_) => WelcomeScreen());
default:
return MaterialPageRoute(
builder: (_) => Center(child: Text('Unknown route')));
}
},
);
}
}
class ErrorScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Center(
child: Text('An error occurred.'),
),
);
}
}
class LoadingScreen extends StatefulWidget {
#override
_LoadingScreenState createState() => _LoadingScreenState();
}
class _LoadingScreenState extends State<LoadingScreen> {
#override
void initState() {
init();
super.initState();
}
init() async {
try {
await Firebase.initializeApp();
if (kDebugMode) {
await FirebaseAuth.instance.useEmulator('http://localhost:1001');
}
final preferences = await SharedPreferences.getInstance();
bool signedIn = preferences.getBool('IS_SIGNED_IN') ?? false;
String landingPath = '/signin';
if (signedIn) {
landingPath = '/welcome';
// Wait for the userChanges to emit a non-null element.
await FirebaseAuth.instance
.userChanges()
.firstWhere((user) => user != null);
}
Navigator.of(context).pushReplacementNamed(landingPath);
} catch (error) {
print(error);
Navigator.of(context).pushReplacementNamed('/error');
}
}
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Center(
child: Text('Loading...'),
),
);
}
}
class SignInScreen extends StatelessWidget {
final _emailController = TextEditingController();
final _passwordController = TextEditingController();
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
children: [
TextField(
controller: _emailController,
decoration: InputDecoration(labelText: 'Email address'),
),
TextField(
decoration: InputDecoration(labelText: 'Password'),
obscureText: true,
controller: _passwordController,
),
ElevatedButton(
onPressed: () => _submit(context),
child: Text('SIGN IN'),
),
],
),
),
);
}
void _submit(BuildContext context) async {
final email = _emailController.text;
final password = _passwordController.text;
FirebaseAuth.instance
.signInWithEmailAndPassword(email: email, password: password)
.then((credential) async {
final preferences = await SharedPreferences.getInstance();
await preferences.setBool('IS_SIGNED_IN', true);
Navigator.of(context).pushReplacementNamed('/welcome');
});
}
}
class WelcomeScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
children: [
Text('Welcome!'),
ElevatedButton(
onPressed: () => _signOut(context),
child: Text('SIGN OUT'),
)
],
),
),
);
}
void _signOut(BuildContext context) {
FirebaseAuth.instance.signOut().then((_) async {
final preferences = await SharedPreferences.getInstance();
await preferences.setBool('IS_SIGNED_IN', false);
Navigator.of(context).pushReplacementNamed('/signin');
});
}
}
If it helps, I'm using version 8.3.0 of the Firebase Javascript libraries in my index.html file.
Am I missing something here?
It turns out the code above is fine. The issue is the emulator. Commenting out these lines of code makes the app behave as expected:
if (kDebugMode) {
FirebaseAuth.instance.useEmulator('http://localhost:1001');
}
I've filed a bug report on FlutterFire's repo about this emulator issue.

Flutter video_player with URL from Firestore Document

I'm trying to play a video from a URL of a Firestore Document. To play a video in Flutter, I have to instantiate its Url in the init() method. I set a default URL to a butterfly video, and the value was supposed to be replaced by the URL obtained from Firestore. (So that it is easy for me to see if the code works). However, the code does not work properly. I got an error that says "NoSuchMethodError: The getter 'value' was called on null".
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:video_player/video_player.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
void main() {
WidgetsFlutterBinding.ensureInitialized();
runApp(MyApp());
}
class MyApp extends StatelessWidget {
// Create the initialization Future outside of build
final Future<FirebaseApp> _initialization = Firebase.initializeApp();
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return FutureBuilder(
future: _initialization,
builder: (context, snapshot) {
// Check for error
if (snapshot.hasError) {
print(snapshot.error);
return Center(
child: Container(
child: Text(
"Something went wrong",
textDirection: TextDirection.ltr,
),
),
);
}
//Once complete, show your application
if (snapshot.connectionState == ConnectionState.done) {
return MaterialApp(
title: 'Flutter Demo',
home: VideoPlayerScreen(),
);
}
return CircularProgressIndicator();
});
}
}
class VideoPlayerScreen extends StatefulWidget {
#override
_VideoPlayerScreenState createState() => _VideoPlayerScreenState();
}
class _VideoPlayerScreenState extends State<VideoPlayerScreen> {
VideoPlayerController _controller;
Future<void> _initializeVideoPlayerFuture;
FirebaseFirestore firestore = FirebaseFirestore.instance;
String videoUrl =
'https://flutter.github.io/assets-for-api-docs/assets/videos/butterfly.mp4';
#override
void initState() {
firestore.collection("videos").get().then((QuerySnapshot querySnapshot) => {
querySnapshot.docs.forEach((doc) {
// _controller.dispose();
videoUrl = doc["videoUrl"];
_controller = VideoPlayerController.network(videoUrl);
_initializeVideoPlayerFuture = _controller.initialize();
print(videoUrl);
})
});
// _controller = VideoPlayerController.network(videoUrl);
// _initializeVideoPlayerFuture = _controller.initialize();
super.initState();
}
#override
void dispose() {
_controller.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Flutter Video Player"),
),
body: FutureBuilder(
future: _initializeVideoPlayerFuture,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
return Column(
children: [
AspectRatio(
aspectRatio: _controller.value.aspectRatio,
child: VideoPlayer(_controller),
),
],
);
} else {
return Center(child: CircularProgressIndicator());
}
},
),
floatingActionButton: FloatingActionButton(
onPressed: () {
setState(() {
if (_controller.value.isPlaying) {
_controller.pause();
} else {
_controller.play();
}
});
},
child: Icon(
_controller.value.isPlaying ? Icons.pause : Icons.play_arrow,
),
),
);
}
}
Try the following:
#override
void initState() {
super.initState();
firestore.collection("videos").get().then((QuerySnapshot querySnapshot) => {
querySnapshot.docs.forEach((doc) {
videoUrl = doc["videoUrl"];
_controller = VideoPlayerController.network(videoUrl);
_initializeVideoPlayerFuture = _controller.initialize().then((_) {
// Ensure the first frame is shown after the video is initialized, even before the play button has been pressed.
setState(() {});
});
});
});
}
Since initialize() is asynchronous, then you can use the method then which will get called when the future completes. Inside the callback, you can call setState() which will trigger a rebuild and notify the framework that the internal state of the widgets has changed .
https://pub.dev/packages/video_player

Flutter Firestore error with BLoC pattern

A newbie in flutter has a lot of stuff that is just starting to figure out now it's BLoC pattern and now I ran into a problem
I can not understand how to fix this error, seems to have written everything correctly
Here generic Interface for all BLoCs
abstract class BlocBase {
void dispose();
}
class BlocProvider<T extends BlocBase> extends StatefulWidget {
BlocProvider({
Key key,
#required this.child,
#required this.bloc,
}) : super(key: key);
final T bloc;
final Widget child;
#override
_BlocProviderState<T> createState() => _BlocProviderState<T>();
static T of<T extends BlocBase>(BuildContext context) {
final type = _typeOf<BlocProvider<T>>();
BlocProvider<T> provider = context.ancestorWidgetOfExactType(type);
return provider.bloc;
}
static Type _typeOf<T>() => T;
}
class _BlocProviderState<T> extends State<BlocProvider<BlocBase>> {
#override
void dispose() {
widget.bloc.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return widget.child;
}
}
Here is the second file in which I use BLoC and where it gives an error
Here I use function validateAndCreateData through which I add Tickets
#override
Widget build(BuildContext context) {
final bloc = BlocProvider.of<TicketsBloc>(context);
return Scaffold(
drawer: MyDrawer(),
appBar: AppBar(
title: Text('Sports'),
backgroundColor: Colors.blueGrey[900],
// automaticallyImplyLeading: false,
actions: <Widget>[
IconButton(
icon: Icon(Icons.share),
tooltip: 'Share',
onPressed: () {
Navigator.of(context).pushNamed('/second_screen');
}),
IconButton(
icon: Icon(Icons.account_circle),
tooltip: 'Your account',
onPressed: () {
Navigator.of(context)
.pushReplacementNamed('/account_screen');
}),
IconButton(
icon: Icon(Icons.add),
tooltip: 'Add Tickets',
onPressed: () => validateAndCreateData(bloc),
)
]),
body: MyTab(),
);
}
void validateAndCreateData(TicketsBloc bloc) async {
bloc.createData(description, image, name, price);
}
Your error mean you don't have access to the bloc. You must wrap your app with the provider. If not you cannot inherited from this.
return BlocProvider(
child: MaterialApp(
title: 'My App',
home: HomeScreen(),
),
);

Resources