So I wanted to add firebase_auth to my app and I ended up here https://firebase.flutter.dev/docs/overview/#initializing-flutterfire
So when I copy the code to set it up (I followed both of the approaches that they give but in this case, I am using the Stateful Widget one) the next error pops up.
══╡ EXCEPTION CAUGHT BY WIDGETS LIBRARY ╞═══════════════════════════════════════════════════════════
The following assertion was thrown building FirebaseLoading:
MediaQuery.of() called with a context that does not contain a MediaQuery.
No MediaQuery ancestor could be found starting from the context that was passed to MediaQuery.of().
This can happen because you do not have a WidgetsApp or MaterialApp widget (those widgets introduce
a MediaQuery), or it can happen if the context you use comes from a widget above those widgets.
The context used was:
Scaffold
The relevant error-causing widget was:
FirebaseLoading
lib\main.dart:63
When the exception was thrown, this was the stack:
Apparently, somehow, the context that this widget creates doesn't have a MediaQuery so when the children (FirebaseLoading()) tries to access it to display a loading message it can't:
Here is the code of FirebaseLoading() btw:
import 'package:flutter/material.dart';
class FirebaseLoading extends StatelessWidget {
const FirebaseLoading({Key key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Text("Loading"),
),
);
}
}
And here is the main class where FirebaseLoading is called:
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:qrcode_test/Models/Cart.dart';
import 'package:qrcode_test/Models/User.dart';
import 'Views/Controller.dart';
import 'Views/FirebaseError.dart';
import 'Views/FirebaseLoading.dart';
void main() {
// WidgetsFlutterBinding.ensureInitialized();
runApp(
MultiProvider(
providers: [
ChangeNotifierProvider(builder: (context) => Cart(bundles: [])),
ChangeNotifierProvider(builder: (context) => User()),
],
child: MyApp(),
),
);
}
class MyApp extends StatefulWidget {
_AppState createState() => _AppState();
}
class _AppState extends State<MyApp> {
// Set default `_initialized` and `_error` state to false
bool _initialized = false;
bool _error = false;
// Define an async function to initialize FlutterFire
void initializeFlutterFire() async {
try {
// Wait for Firebase to initialize and set `_initialized` state to true
await Firebase.initializeApp();
setState(() {
_initialized = true;
});
} catch (e) {
// Set `_error` state to true if Firebase initialization fails
setState(() {
_error = true;
});
}
}
#override
void initState() {
initializeFlutterFire();
super.initState();
}
#override
Widget build(BuildContext context) {
// Show error message if initialization failed
if (_error) {
return FirebaseError();
}
// Show a loader until FlutterFire is initialized
if (!_initialized) {
return FirebaseLoading();
}
return Controller();
}
}
I don't know if the multiproviders that I was using in the app can be causing any kind of problem but I don't think so.
Okay, it's not enought to call the widget over there, it has to be inside of a MaterialWidget like so:
#override
Widget build(BuildContext context) {
// Show error message if initialization failed
if (_error) {
return new MaterialApp(
title: "My Cashier",
theme: defaultTheme,
home: new FirebaseError(),
);
}
// Show a loader until FlutterFire is initialized
if (!_initialized) {
return new MaterialApp(
title: "My Cashier",
theme: defaultTheme,
home: new FirebaseLoading(),
);
}
return new MaterialApp(
title: "My Cashier",
theme: defaultTheme,
home: new Controller(),
);
}
There is for sure a way not to repeat the MaterialApp, the title and the theme but I am tired...
Related
I managed to connect my application to the firebase storage, I managed to see the documents that I uploaded to it. But I can't link it to the realtime database.
Database's rules :
{
"rules": {
".read": true,
".write": true,
}
}
My code to test:
import 'package:flutter/material.dart';
import 'package:firebase_database/firebase_database.dart';
class Bddtest extends StatefulWidget {
#override
_Bddtest createState() => _Bddtest();
}
class _Bddtest extends State<Bddtest> {
var adder = 0;
final ref = FirebaseDatabase().reference();
void addData() {
ref.child("test/").push().set({'add number': adder});
adder++;
}
void printFirebase() {
ref.once().then((DataSnapshot snapshot) {
print('Data : ${snapshot.value}');
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Firebase Demo"),
),
body: FutureBuilder(builder: (context, snapshot) {
if (snapshot.hasError) {
return Text(snapshot.error.toString());
} else {
return Container(
child: Column(
children: <Widget>[
Center(
child: ElevatedButton(
child: Text("Save to Database"),
onPressed: () {
addData();
printFirebase();
})),
],
),
);
}
}),
);
}
}
I have no error in my terminal and no connection attempt on the firebase side.
My main :
// #dart=2.9 (because i need to use a non-null friendly widget)
import 'package:racpresence/bddtest.dart';
import 'calendrier.dart';
import 'coursdujour.dart';
import 'parametres.dart';
import 'bddtest.dart';
import 'package:flutter/cupertino.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/material.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
Firebase.initializeApp();
runApp(MyApp());
}
class MyApp extends StatelessWidget {
...
}
class MyHomePage extends StatefulWidget {
...
}
class _MyHomePageState extends State<MyHomePage> {
int _selectedIndex = 0;
final tabs = [
...
];
void _onItemTapped(int index) {
setState(() {
_selectedIndex = index;
});
}
#override
Widget build(BuildContext context) {
...
}
}
Thank for your help and sorry if there is weird name, I'm french.
UPDATE: I succeeded in connecting the application to the cloud firestore. I had to add "await" before Firebase.initializeApp ().
But I still have not succeeded for the realtime database.
If someone ask him, i resolve it with that
final ref = FirebaseDatabase(databaseURL:
$(url of my database) )
.reference()
The error received:
NoSuchMethodError: invalid member on null: 'collection'
I'm using the Flutter Provider Package 4.3.2+2 for complex state management.
I get my data from Firebase, this is the data structure:
This is the provider class:
class ClientsProvider extends ChangeNotifier {
FirebaseFirestore _fs;
StreamSubscription<QuerySnapshot> _stream;
List<QueryDocumentSnapshot> clients = [];
ClientsProvider() {
_stream = _fs.collection("clients").snapshots().listen((event) {//THIS IS WHERE THE ERROR POINTS
clients = event.docs;
notifyListeners();
});
}
#override
void dispose() {
super.dispose();
_stream.cancel();
}
}
This is the parent widget:
class _ParentState extends State<Parent> {
#override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
Provider<ClientsProvider>(
create: (context) => ClientsProvider()),
ChangeNotifierProvider<MyProvider>(
create: (context) => MyProvider()),
],
child: Dashboard(),//This widget contains the Child() widget two widgets down.
);
}
}
This is the child widget that needs to be updated when the firebase snapshot updates:
class _ChildState extends State<Child> {
#override
Widget build(BuildContext context) {
return Column(
children: [
_buildAddNew(),
Consumer<ClientsProvider>(
builder: (context, clientProvider, child) {
return Container(child: Text(clientProvider.clients.toString()));
},
),
],
);
}
}
Once again, the error received:
NoSuchMethodError: invalid member on null: 'collection'
The data isn't null, why am I receiving this error?
_fs is null because you never assigned it. Perhaps you meant to do this, as shown in the documentation:
FirebaseFirestore _fs = FirebaseFirestore.instance;
This question already has answers here:
No Firebase App '[DEFAULT]' has been created - call Firebase.initializeApp() in Flutter and Firebase
(27 answers)
Closed 2 years ago.
I started to learn flutter again (started some time ago but stopped). You may find my code below. If I run the app on my smartphone it gives me the error: [core/no-app] No Firebase App['DEFAULT'} has been created - call Firebase.initializeApp(). I read in the documentation, but since I use the recent versions firebase_core: ^0.5.0, firebase_auth: ^0.18.0+1, cloud_firestore: ^0.14.0+2
it seems like the documentation isnt finished or I didnt get it. Where do I need to initialize that one? Why isnt that one enough: CollectionReference users = FirebaseFirestore.instance.collection('users');
timeline.dart (code is mostly from flutter getting started):
import 'package:flutter/material.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:example/widgets/header.dart';
import 'package:example/widgets/progress.dart';
class Timeline extends StatefulWidget {
#override
_TimelineState createState() => _TimelineState();
}
class _TimelineState extends State<Timeline> {
#override
void initState() {
// getUserById();
super.initState();
}
#override
Widget build(BuildContext context) {
CollectionReference users = FirebaseFirestore.instance.collection('users');
return StreamBuilder<QuerySnapshot>(
stream: users.snapshots(),
builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
if (snapshot.hasError) {
return Text('Something went wrong');
}
if (snapshot.connectionState == ConnectionState.waiting) {
return Text("Loading");
}
return new ListView(
children: snapshot.data.documents.map((DocumentSnapshot document) {
return new ListTile(
title: new Text(document.data()['username']),
subtitle: new Text(document.data()['posts_count']),
);
}).toList(),
);
},
);
}
}
This is my code for main.dart
import 'package:flutter/material.dart';
import 'package:firebase_core/firebase_core.dart';
import 'pages/home.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Example',
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: Colors.red,
accentColor: Colors.blue,
),
home: Home(),
);
}
}
Thank you so much in advance!
In your main.dart, add the following
void main() async {
WidgetsFlutterBinding.ensureInitialized(); //add this
await Firebase.initializeApp(); //initialize here
runApp(MyApp());
}
Visit the new docs to learn more about working with the firebase packages
You can modify your MyApp widget like this, to make things work.
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
home: FutureBuilder(
future: Firebase.initializeApp(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
if (snapshot.hasError) {
print(snapshot.error.toString());
return Center(child: Text('Error'));
} else {
// ! RETURN THE SCREEN YOU WANT HERE
return Timeline();
}
},
),
);
}
}
I need to load the saved themes from shared preference async from app start. After it loaded, replace the placeholder two themes. But below code failed because even it loaded the results for several themes, the app always displays the two themes.
void main() => runApp(MyApp());
class MyApp extends StatefulWidget {
#override
State<StatefulWidget> createState() {
return MyAppState();
}
}
class MyAppState extends State<MyApp> {
final bloc = AppThemeBloc();
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
bloc.getAppThemes();
return buildApp();
}
Widget buildApp() {
return StreamBuilder<List<AppTheme>>(
stream: bloc.subject.stream,
builder: (context, AsyncSnapshot<List<AppTheme>> snapshot) {
if (snapshot.hasData) {
return ThemeProvider(
saveThemesOnChange: true,
loadThemeOnInit: true,
themes: snapshot.data,
child: MaterialApp(
home: ThemeConsumer(
child: HomePage(),
),
),
);
} else {
return ThemeProvider(
saveThemesOnChange: true,
loadThemeOnInit: true,
themes: [
AppTheme.light(),
AppTheme.dark(),
],
child: MaterialApp(
home: ThemeConsumer(
child: HomePage(),
),
),
);
}
});
}
}
After the data is received and the streambuilder is refreshed which updates the state so build function is being executed again thus calling:
bloc.getAppThemes();
again which waits for any data to pass in the stream returning your placeholder themes, place bloc.getAppThemes function in the initState function:
#override
void initState() {
super.initState();
bloc.getAppThemes();
}
I am using Redux within Flutter (and I am just starting to learn both). I have been trying to figure out how to switch between the pages of a PageView using the PageView's PageController.
However, whenever I try to use the PageController.jumpToPage() function, I get an exception stating:
"The following assertion was thrown while finalizing the widget tree: setState() or markNeedsBuild() called when widget tree was locked."
When I attempt to call the PageController.jumpToPage() in my reducer, it does navigate to the page within the pageview; but the exception gets thrown.
I have also tried just building a new PageController in the reducer, and just setting the PageController's initial page property to the desired page, but that didn't seem to do anything.
I have run out of ideas on how to figure this out on my own, so I thought I would ask here. Any help would be appreciated.
I have thrown together a quick sample showing what I am trying to do:
import 'package:flutter/material.dart';
import 'package:redux/redux.dart';
import 'package:flutter_redux/flutter_redux.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
final store = Store<AppState>(appReducer,
initialState: AppState.initial(), middleware: []);
#override
Widget build(BuildContext context) {
return StoreProvider(
store: store,
child: MaterialApp(
title: 'PageView Example With Redux',
home: MyPageViewContainer(),
),
);
}
}
class AppState {
final List<Widget> pageViewList;
final PageController pageController;
AppState({
this.pageViewList,
this.pageController,
});
factory AppState.initial() {
return AppState(
pageViewList: [
PageOne(),
PageTwo(),
],
pageController: PageController(initialPage: 0),
);
}
AppState copyWith({
List<Widget> pageViewList,
PageController pageController,
}) {
return AppState(
pageViewList: pageViewList ?? this.pageViewList,
pageController: pageController ?? this.pageController,
);
}
}
AppState appReducer(AppState state, action) {
if (action is NavigateToPageOneAction) {
state.pageController.jumpToPage(0);
return state;
}
else if (action is NavigateToPageTwoAction) {
state.pageController.jumpToPage(1);
return state;
}
else {
return state;
}
}
class NavigateToPageOneAction {}
class NavigateToPageTwoAction {}
class MyPageView extends StatelessWidget {
final List<Widget> pageViewList;
final PageController pageController;
final Function onPageChanged;
MyPageView({
this.pageViewList,
this.pageController,
this.onPageChanged,
});
#override
Widget build(BuildContext context) {
return PageView(
controller: pageController,
children: pageViewList,
onPageChanged: onPageChanged,
);
}
}
class MyPageViewContainer extends StatelessWidget {
MyPageViewContainer({Key key}) : super(key: key);
#override
Widget build(BuildContext context) {
return StoreConnector<AppState, _MyPageViewModel>(
converter: (Store<AppState> store) => _MyPageViewModel.create(store),
builder: (BuildContext context, _MyPageViewModel vm) {
return MyPageView(
pageViewList: vm.pageViewList,
pageController: vm.pageController,
);
},
);
}
}
class _MyPageViewModel {
final List<Widget> pageViewList;
final PageController pageController;
final Function onPageChanged;
_MyPageViewModel({
this.pageViewList,
this.pageController,
this.onPageChanged,
});
factory _MyPageViewModel.create(Store<AppState> store) {
_onPageChanged() {}
return _MyPageViewModel(
pageViewList: store.state.pageViewList,
pageController: store.state.pageController,
onPageChanged: _onPageChanged(),
);
}
}
class PageOne extends StatelessWidget {
PageOne();
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Page One"),
),
backgroundColor: Colors.black,
body: Column(),
drawer: MyDrawer(),
);
}
}
class PageTwo extends StatelessWidget {
PageTwo();
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Page Two"),
),
backgroundColor: Colors.blue,
body: Column(),
drawer: MyDrawer(),
);
}
}
class MyDrawer extends StatelessWidget {
MyDrawer({Key key}) : super(key: key);
#override
Widget build(BuildContext context) {
return StoreConnector<AppState, _MyDrawerViewModel>(
converter: (Store<AppState> store) => _MyDrawerViewModel.create(store),
builder: (BuildContext context, _MyDrawerViewModel vm) {
return Drawer(
child: ListView(
children: <Widget>[
Container(
child: ListTile(
title: Text(vm.pageOneText),
onTap: vm.pageOneOnTap,
),
),
Container(
child: ListTile(
title: Text(vm.pageTwoText),
onTap: vm.pageTwoOnTap,
),
),
],
),
);
},
);
}
}
class _MyDrawerViewModel {
final String pageOneText;
final String pageTwoText;
final Function pageOneOnTap;
final Function pageTwoOnTap;
_MyDrawerViewModel({
this.pageOneText,
this.pageTwoText,
this.pageOneOnTap,
this.pageTwoOnTap,
});
factory _MyDrawerViewModel.create(Store<AppState> store) {
_goToPageOne() {
store.dispatch(NavigateToPageOneAction());
}
_goToPageTwo() {
store.dispatch(NavigateToPageTwoAction());
}
return _MyDrawerViewModel(
pageOneText: "Page One",
pageTwoText: "Page Two",
pageOneOnTap: _goToPageOne,
pageTwoOnTap: _goToPageTwo,
);
}
}
I seem to have figured out how to solve my problem. I saw an answer in this post: Flutter: setState() or markNeedsBuild() called when widget tree was locked... during orientation change
In that post the OP was encountering the same error when changing between portrait and landscape mode while the Drawer was open. The answer in that post suggested calling Navigator.pop() (which closes the Drawer) before changing view modes.
So I gave that a try and closed my Drawer using the Navigator.pop() prior to using the PageController's .jumpToPage method. This seems to work, and allows me to navigate between pages of the PageView using onTap events from the Drawer, without throwing the "The following assertion was thrown while finalizing the widget tree: setState() or markNeedsBuild() called when widget tree was locked" exception.
I assume that this means that while the Drawer is open, the widget tree is placed into a locked state.
Hopefully this helps someone, as it took me a while to figure out.
#Blau
Sometimes an event happened outside any widgets you built. e.g. (1) A timer that increment a 'Global Counter', this counter will be shown in many pages/widgets (2) A message sent from the socket server, on receiving this message/event, the user may be anywhere(any pages/widgets), and you don't know where to 'setState' (Or the widget is actually not there because the user is not at that page)
I've built 2 examples that demonstrate how to use Redux to solve this kind of problems:
Example 1: (Use a multi-thread timer to 'setState' a widget when an external event fires)
https://github.com/lhcdims/statemanagement01
Example 2: (Use Redux to refresh a widget when an external event fires)
https://github.com/lhcdims/statemanagement02
Demo Screen Shot: