Flutter Firestore error with BLoC pattern - firebase

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

Related

Not a subtype of type 'String?' issue

Basically, I have a mainscreen and a HomeTabPage. I want to bind the mainscreen keys with the hometabpage such that I can save the state of my page when navigating between the tabs
This is my code so far
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return ChangeNotifierProvider(
create: (context) => AppData(),
child: MaterialApp(
title: 'Test',
theme: ThemeData(
// fontFamily: "Brand Bold",
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
initialRoute: FirebaseAuth.instance.currentUser == null
? LoginScreen.idScreen
: MainScreen(
map: HomeTabPage(),
),
),
);
}
}
The mainScreen has a tab controller
class MainScreen extends StatefulWidget {
//const MainScreen({ Key? key }) : super(key: key);
static const String idScreen = "mainScreen";
final Widget map;
const MainScreen({Key key, this.map}) : super(key: key);
#override
_MainScreenState createState() => _MainScreenState();
}
class _MainScreenState extends State<MainScreen>
with SingleTickerProviderStateMixin {
TabController tabController;
int selectedIndex = 0;
void onItemClicked(int index) {
setState(() {
selectedIndex = index;
tabController.index = selectedIndex;
});
}
#override
void initState() {
super.initState();
tabController = TabController(length: 4, vsync: this);
}
#override
void dispose() {
super.dispose();
tabController.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: TabBarView(
physics: NeverScrollableScrollPhysics(),
controller: tabController,
children: [
widget.map,
//HomeTabPage(),
EarningTabPage(),
RatingTabPage(),
ProfileTabPage(),
],
),
bottomNavigationBar: BottomNavigationBar(
items: [
BottomNavigationBarItem(icon: Icon(Icons.home), label: "Home"),
BottomNavigationBarItem(
icon: Icon(Icons.credit_card), label: "Earnings"),
BottomNavigationBarItem(icon: Icon(Icons.star), label: "Ratings"),
BottomNavigationBarItem(icon: Icon(Icons.person), label: "Account"),
],
unselectedItemColor: Colors.black54,
selectedItemColor: Colors.yellow,
type: BottomNavigationBarType.fixed,
selectedLabelStyle: TextStyle(fontSize: 12.0),
showUnselectedLabels: true,
currentIndex: selectedIndex,
onTap: onItemClicked,
),
);
}
}
The HomeTabPage
class HomeTabPage extends StatefulWidget {
const HomeTabPage({Key key}) : super(key: key);
#override
_HomeTabPageState createState() => _HomeTabPageState();
}
class _HomeTabPageState extends State<HomeTabPage>
with AutomaticKeepAliveClientMixin {
#override
bool get wantKeepAlive => true;
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
super.build(context);
return Stack(children: [
]);
}
}
The above code gives me this error
type 'MainScreen' is not a subtype of type 'String?'
when I change the main.dart file to this, it works fine.
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Test',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MainScreen(map: HomeTabPage())
}
}
But I need to use it with firebaseAuth, How can I do it?
On initialRoute it takes string.
initialRoute: FirebaseAuth.instance.currentUser == null
? LoginScreen.idScreen
: MainScreen.idScreen,
),
And map will get default value
Widget? map; // or just Widget map
const MainScreen({Key? key, this.map =const HomeTabPage()}) : super(key: key);
More about initialRoute

No MediaQuery widget ancestor found. All the answers on the service did not help (((

Making a list with adding items to the database. After switching from main.dart to the page with a list, it does not open, it writes an error.enter image description here
import 'package:flutter/material.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_core/firebase_core.dart';
class Home extends StatefulWidget {
const Home({Key? key}) : super(key: key);
#override
State<Home> createState() => _HomeState();
}
class _HomeState extends State<Home> {
late String _userToDo;
List todoList = [];
void initFirebase() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp(Home());
}
#override
void initState() {
super.initState();
initFirebase();
todoList.addAll(['Milk', 'Car', 'Sugar']);
}
void _menuOpen() {
Navigator.of(context).push(
MaterialPageRoute(builder: (BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Menu'),),
body: Row(
children: [
Padding(padding: EdgeInsets.only(left: 15)),
ElevatedButton(onPressed: () {
Navigator.pop(context);
Navigator.pushNamedAndRemoveUntil(context, '/', (route) => false);
},
child: Text('Home')),
Padding(padding: EdgeInsets.only(left: 15)),
Text('Home old')
],
)
);
})
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.grey[900],
appBar: AppBar(
title: Text('Список дел'),
actions: [
IconButton(onPressed: _menuOpen,
icon: Icon(Icons.menu_outlined),
)
],
),
body: ListView.builder(
itemCount: todoList.length,
itemBuilder: (BuildContext context, int index){
return Dismissible(
key: Key(todoList[index]),
child: Card(
child: ListTile(
title: Text(todoList[index]),
trailing: IconButton(
icon: Icon(Icons.delete_sweep,
color: Colors.redAccent,
), onPressed: () {
setState(() {
todoList.removeAt(index);
});
},
)
),
),
onDismissed: (direction) {
// if(direction == DismissDirection.startToEnd)
setState(() {
todoList.removeAt(index);
});
},
);
}
),
floatingActionButton: FloatingActionButton(
backgroundColor: Colors.green,
onPressed: () {
showDialog(context: context, builder: (BuildContext context){
return AlertDialog(
title: Text('Добавить'),
content: TextField(
onChanged: (String value){
_userToDo = value;
},
),
actions: [
ElevatedButton(onPressed: (){
FirebaseFirestore.instance.collection('items').add({'item': _userToDo});
Navigator.of(context).pop();
}, child: Text('Добавить')
)
],
);
});
},
child: Icon(Icons.add_comment_outlined,
color: Colors.white,
),
),
);
}
}
Everyone knows the error.
The following assertion was thrown building Home(state:
_HomeState#17f50): No MediaQuery widget ancestor found.
Scaffold widgets require a MediaQuery widget ancestor. The specific
widget that could not find a MediaQuery ancestor was: Scaffold dirty
state: ScaffoldState#4d9ee(lifecycle state: initialized, tickers:
tracking 2 tickers) The ownership chain for the affected widget is:
"Scaffold ← Home ← [root]"
No MediaQuery ancestor could be found starting from the context that
was passed to MediaQuery.of(). This can happen because you have not
added a WidgetsApp, CupertinoApp, or MaterialApp widget (those widgets
introduce a MediaQuery), or it can happen if the context you use comes
from a widget above those widgets.
Set according to your advice. Navigation and pop-up window stopped working.
import 'package:flutter/material.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_core/firebase_core.dart';
class Home extends StatefulWidget {
const Home({Key? key}) : super(key: key);
#override
State<Home> createState() => _HomeState();
}
class _HomeState extends State<Home> {
late String _userToDo;
List todoList = [];
void initFirebase() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp(Home());
}
#override
void initState() {
super.initState();
initFirebase();
todoList.addAll(['Milk', 'Car', 'Sugar']);
}
void _menuOpen() {
Navigator.of(context).push(
MaterialPageRoute(builder: (BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Menu'),),
body: Row(
children: [
Padding(padding: EdgeInsets.only(left: 15)),
ElevatedButton(onPressed: () {
Navigator.pop(context);
Navigator.pushNamedAndRemoveUntil(context, '/', (route) => false);
},
child: Text('Home')),
Padding(padding: EdgeInsets.only(left: 15)),
Text('Home old')
],
)
);
})
);
}
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
backgroundColor: Colors.grey[900],
appBar: AppBar(
title: Text('Список дел'),
actions: [
IconButton(onPressed: _menuOpen,
icon: Icon(Icons.menu_outlined),
)
],
),
body: ListView.builder(
itemCount: todoList.length,
itemBuilder: (BuildContext context, int index){
return Dismissible(
key: Key(todoList[index]),
child: Card(
child: ListTile(
title: Text(todoList[index]),
trailing: IconButton(
icon: Icon(Icons.delete_sweep,
color: Colors.redAccent,
), onPressed: () {
setState(() {
todoList.removeAt(index);
});
},
)
),
),
onDismissed: (direction) {
// if(direction == DismissDirection.startToEnd)
setState(() {
todoList.removeAt(index);
});
},
);
}
),
floatingActionButton: FloatingActionButton(
backgroundColor: Colors.green,
onPressed: () {
showDialog(context: context, builder: (BuildContext context){
return AlertDialog(
title: Text('Добавить'),
content: TextField(
onChanged: (String value){
_userToDo = value;
},
),
actions: [
ElevatedButton(onPressed: (){
FirebaseFirestore.instance.collection('items').add({'item': _userToDo});
Navigator.of(context).pop();
}, child: Text('Добавить')
)
],
);
});
},
child: Icon(Icons.add_comment_outlined,
color: Colors.white,
),
),
),
);
}
}
The following assertion was thrown while handling a gesture: No
MaterialLocalizations found.
Home widgets require MaterialLocalizations to be provided by a
Localizations widget ancestor. The material library uses Localizations
to generate messages, labels, and abbreviations.
To introduce a MaterialLocalizations, either use a MaterialApp at the
root of your application to include them automatically, or add a
Localization widget with a MaterialLocalizations delegate.
The specific widget that could not find a MaterialLocalizations
ancestor was: Home state: _HomeState#8899d The ancestors of this
widget were: : [root]
renderObject: RenderView#1dbbb
void initFirebase() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp(Home());
}
With that runApp call, you are removing your entire widget tree and replacing it with a tree rooted at a Home widget. This means that you are unable to access the MaterialApp widget that is presumably built by your App widget elsewhere in your app.
To fix this, move the first two lines of this method to your main method before runApp, and remove the entire method from the Home widget.
Part of the error says: This can happen because you have not added a WidgetsApp, CupertinoApp, or MaterialApp widget.
So in your Build method, you can wrap your Scaffold with a MaterialApp() and it should work.
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(...),
);
}

User not able to sign out in firebase Flutter application

D/FirebaseAuth( 7994): Notifying id token listeners about a sign-out event.
D/FirebaseAuth( 7994): Notifying auth state listeners about a sign-out event.
class LandingPage extends StatefulWidget {
#override
_LandingPageState createState() => _LandingPageState();
}
class _LandingPageState extends State<LandingPage> {
User _user;
#override
void initState() {
super.initState();
_updateUser(FirebaseAuth.instance.currentUser);
}
void _updateUser(User user) {
setState(() {
_user = user;
});
}
#override
Widget build(BuildContext context) {
if (_user == null) {
return SignInPage(
onSignIn: _updateUser,
);
}
return HomePage(
onSignOut: () => _updateUser(null),
);
}
}
Home page Code:-
class HomePage extends StatelessWidget {
HomePage({#required this.onSignOut});
final VoidCallback onSignOut;
Future<void> _signOut() async {
try {
await FirebaseAuth.instance.signOut();
} catch (e) {
print(e.toString()) ;
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Home Page'),
actions: <Widget>[
FlatButton(
child: Text(
'Logout',
style: TextStyle(
fontSize: 18.0,
color: Colors.white,
),
),
onPressed: _signOut,
),
],
),
);
}
}
this is my code but the user is not able to log out of my application.Can somebody tell me what to fix
Try to await the signout call:
class HomePage extends StatelessWidget {
HomePage({#required this.onSignOut});
final VoidCallback onSignOut;
Future<void> _signOut() async {
try {
await FirebaseAuth.instance.signOut();
} catch (e) {
print(e.toString()) ;
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Home Page'),
actions: <Widget>[
FlatButton(
child: Text(
'Logout',
style: TextStyle(
fontSize: 18.0,
color: Colors.white,
),
),
onPressed: () async => await _signOut(), // Updated code
),
],
),
);
}
}

How to pass data across Stateful widget?

So my question is i get data from firebase in first widget, then i click and open a bottomsheet through void -> another stateful widget, how can i pass the snapshot data from first widget to the other one?
Below code is not working...
....
Widget build(BuildContext context) {
return Container(
ElevatedButton(
child: Text('Request a tour'),
onPressed: () {
displayBottomSheet(context, widget.snapshot.data()["author"]);
},
),
);
void displayBottomSheet(BuildContext context, String author) { //updated
showModalBottomSheet(
context: context,
builder: (ctx) {
return BottomSheetWidget(author); //updated
});
}
NEW ERROR: Too many positional arguments: 0 expected, but 1 found.
class BottomSheetWidget extends StatefulWidget {
final String author; //updated
BottomSheetWidget({this.author}); //updated
#override
class _BottomSheetWidgetState createState() => _BottomSheetWidgetState();
}
class _BottomSheetWidgetState extends State<BottomSheetWidget> {
Widget build(BuildContext context) {
return Container(
new ElevatedButton(
child: Text('Send'),
onPressed: () {
requestTour(widget.author); //updated
},
),
.....
}
requestTour(String userName) async {
...
}
class BottomSheetWidget extends StatefulWidget {
final String author; //updated
BottomSheetWidget(this.author); //<-- remove {}
#override
class _BottomSheetWidgetState createState() => _BottomSheetWidgetState();
}
class _BottomSheetWidgetState extends State<BottomSheetWidget> {
Widget build(BuildContext context) {
return Container(
new ElevatedButton(
child: Text('Send'),
onPressed: () {
requestTour(widget.author); //updated
},
),
.....
}
requestTour(String userName) async {
...
}
Just remove curly braces for new arrived error:
replace BottomSheetWidget({this.author}); with BottomSheetWidget(this.author);

How to share provider data from streambuilder via different pages (contextes)

I want to have data from firebase in realtime on a widget. When I try to use a StreamProvider and then use Navigator.push(), the pushed widget can't get the value with Provider.of(context).
I tried putting the StreamProvider as the parent of MaterialApp. This works but the user needs to be logged in order for the Stream to get the data of the user.
I also tried using a ScopedModel. This works as well, but I don't know if this is the best approach to do this.
I would like to avoid using a global StreamProvider and would like to have an efficient solution (as little reads from firebase as possible)
main.dart
void main() => runApp(MyApp());
final GlobalKey<ScaffoldState> mainScaffoldKey = GlobalKey<ScaffoldState>();
final GlobalKey<ScaffoldState> authScaffoldKey = GlobalKey<ScaffoldState>();
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return ScopedModel<ScreenModel>(
model: ScreenModel(),
child: MultiProvider(
providers: [
StreamProvider<User>.value(value: authService.userDoc,),
StreamProvider<bool>.value(value: authService.loading.asBroadcastStream())
],
child: MaterialApp(
title: "ListAssist",
theme: ThemeData(
primarySwatch: Colors.indigo,
),
home: MainApp()
),
)
);
}
}
class MainApp extends StatefulWidget {
#override
_MainAppState createState() => _MainAppState();
}
class _MainAppState extends State<MainApp> {
#override
Widget build(BuildContext context) {
User user = Provider.of<User>(context);
bool loading = Provider.of<bool>(context);
return AnimatedSwitcher(
duration: Duration(milliseconds: 600),
child: user != null ?
StreamProvider<Group>.value(
value: databaseService.streamGroupsFromUser(),
child: Scaffold(
key: mainScaffoldKey,
body: Body(),
drawer: Sidebar(),
),
) : Scaffold(
key: authScaffoldKey,
body: AnimatedSwitcher(
duration: Duration(milliseconds: 600),
child: loading ? SpinKitDoubleBounce(color: Colors.blueAccent) : AuthenticationPage(),
),
resizeToAvoidBottomInset: false,
)
);
}
}
class Body extends StatefulWidget {
createState() => _Body();
}
class _Body extends State<Body> {
#override
Widget build(BuildContext context) {
return ScopedModelDescendant<ScreenModel>(
builder: (context, child, model) => model.screen
);
}
}
In the Sidebar I can change to GroupView and the Provider still works.
sidebar.dart (important part)
onTap: () {
ScreenModel.of(context).setScreen(GroupView(), "Gruppen");
Navigator.pop(context);
},
The GroupView has GroupItem in it
group-item.dart (important part)
onTap: () => Navigator.push(
context,
MaterialPageRoute(builder: (context) {
return GroupDetail();
}),
)
When I try to use Group group = Provider.of<Group>(context); in GroupDetail or a child widget of it, it says that it cannot find any Provider for the context.
Here is the repository.
I figured out how to do it. I used a package called custom_navigator.
In sidebar.dart I changed the child when someone changes to the group view to the following:
StreamProvider<Group>.value(
value: databaseService.streamGroupsFromUser(user.uid),
child: CustomNavigator(
home: GroupView(),
pageRoute: PageRoutes.materialPageRoute,
)
)
With the CustomNavigator I can still use Provider.of<Group>(context) to get the data, even after a Navigator.push().

Resources