I created an app with Flutter that contains a ListView that i build up with a StreamBuilder working with Firestore.
The code is written cleanly and ready to go but as soon as i run the app on my device, it tells me that " [The app] has been ended."
Android Studio doesnt really give out an error message. Within my logcat it tells me "Please configure Android SDK". Unfortunately i dont know how to proceed from here.
I made the Google Services setup step by step and the app is registered for Firestore usage.
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'Flutter Demo',
theme: new ThemeData(
primarySwatch: Colors.purple,
),
home: new MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => new _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
// ignore: non_constant_identifier_names
Stream<QuerySnapshot> tours_collection;
#override
void initState() {
super.initState();
tours_collection = Firestore.instance.collection('tours_collection').snapshots();
}
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text("ToursList"),
),
body: new ToursList(),
);
}
}
class ToursList extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new StreamBuilder(
stream: Firestore.instance.collection('tours_collection').snapshots(),
builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
if (!snapshot.hasData) return new Text('Loading...');
return new ListView(
children: snapshot.data.documents.map((document) {
return new ListTile(
title: new Text(document['name']),
);
}).toList(),
);
},
);
}
}
Related
I have checked some threads here on StackOverlow but they dont fix my problem.The suggestions are
Create a new Stateless/Stateful widget and pass it to the home parameter OR
Use the Builder widget and pass it to the home parameter.
which I already did.
This is my main.dart file
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) {
ScreenUtil.init(context,designSize: Size(360,640));
return MaterialApp(
title: 'Flutter Demo',
home: HomeScreen()
);
}
}
And this is home.dart file
class HomeScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
body: Row(
children: [
buildLeftColumn(),
SizedBox(
width: 20.w,
),
// buildRightColumn(),
],
)),
);
}
buildLeftColumn() {
return Container();
}
So. what am i doing wrong.Could you please help
If you like to use ScreenUtil.init(...) you can solve this issue calling it on HomeScreen widget(context).
class HomeScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
ScreenUtil.init(context, designSize: Size(360, 640));
To use it before MaterialApp you can use ScreenUtilInit widget.
Widget build(BuildContext context) {
return ScreenUtilInit(
designSize: Size(360, 640),
builder: (context, child) => MaterialApp(
title: 'Flutter Demo',
home: HomeScreen(),
),
);
}
I am using bloc to manage my app state, I want to provide the bloc for all my app pages so I have inserted in the top of the widget tree so I can use it from any place in the widget tree, I have used it as the follows
1- main page
class MyApp extends StatefulWidget {
#override
State<StatefulWidget> createState() {
return MyAppState();
}}
class MyAppState extends State<MyApp>{
#override
Widget build(BuildContext context) {
return BlocProvider<MyBloc>(
create: (BuildContext context) {
return MyBloc();
},
child: MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: secondPage()),
);
}
}
2- secondPage:
class SecondPage extends StatefulWidget {
#override
State<StatefulWidget> createState() {
return SecondPage State();
}
}
class SecondPage State extends State<SecondPage > {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('secondPage')),
body: BlocBuilder<CityBloc, CityState>(
builder: (BuildContext context, CityState state) {
.......
},));}}
but the flutter display an error that
BlocProvider.of() called with a context that does not contain a Bloc of type MyBloc
and this is a screenshot of the app's widgets tree
, what is the error, I want to provide mybloc for all widgets
note: the app run ok if I write the MainPage class and the secondPage class in the same page, but when I separate them the error appears
I was shocked by the solution, the problem was only in import, I have replaced
import '../blocs/blocs.dart';
With
import 'package: loony_trips / blocs / blocs.dart';
And everything was fixed, even though the two sentences were supposed to be the same
I want to keep the widget's state in Scaffold.drawer. The Scaffold.drawer is a custom widget, which has a RaiseButton in it.
When click the button, the text in the button changed.
But when the drawer is closed, and reopen the drawer, the changed text is reseted.
I have use " with AutomaticKeepAliveClientMixin<> " in my custom Drawer, but it does't work.
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key}) : super(key: key);
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Flutter Demo"),
),
drawer: Drawer(child: CustomDrawer(),),
body: Center(
child: Text("Flutter Demo"),
),
);
}
}
class CustomDrawer extends StatefulWidget {
#override
State<StatefulWidget> createState() {
return _CustomDrawerState();
}
}
class _CustomDrawerState extends State<CustomDrawer> with AutomaticKeepAliveClientMixin<CustomDrawer> {
String btnText = "Click!";
#override
bool get wantKeepAlive => true;
#override
Widget build(BuildContext context) {
super.build(context);
return Center(
child: RaisedButton(onPressed: () {
setState(() {
btnText = "Clicked!!";
});
}, child: Text(btnText),),
);
}
}
I expect the widget's state can keep, even if the Drawer is closed.
Create a separate widget for the drawer and just use in anywhere you need to.
Manage the Drawer State with a Provider
class DrawerStateInfo with ChangeNotifier {
int _currentDrawer = 0;
int get getCurrentDrawer => _currentDrawer;
void setCurrentDrawer(int drawer) {
_currentDrawer = drawer;
notifyListeners();
}
void increment() {
notifyListeners();
}
}
Adding State Management to the Widget tree
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MultiProvider(
child: MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.teal,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
),
providers: <SingleChildCloneableWidget>[
ChangeNotifierProvider<DrawerStateInfo>(
builder: (_) => DrawerStateInfo()),
],
);
}
}
Creating The Drawer Widget for reuse in application
class MyDrawer extends StatelessWidget {
MyDrawer(this.currentPage);
final String currentPage;
#override
Widget build(BuildContext context) {
var currentDrawer = Provider.of<DrawerStateInfo>(context).getCurrentDrawer;
return Drawer(
child: ListView(
children: <Widget>[
ListTile(
title: Text(
"Home",
style: currentDrawer == 0
? TextStyle(fontWeight: FontWeight.bold)
: TextStyle(fontWeight: FontWeight.normal),
),
trailing: Icon(Icons.arrow_forward),
onTap: () {
Navigator.of(context).pop();
if (this.currentPage == "Home") return;
Provider.of<DrawerStateInfo>(context).setCurrentDrawer(0);
Navigator.of(context).pushReplacement(MaterialPageRoute(
builder: (BuildContext context) =>
MyHomePage(title: "Home")));
},
),
ListTile(
title: Text(
"About",
style: currentDrawer == 1
? TextStyle(fontWeight: FontWeight.bold)
: TextStyle(fontWeight: FontWeight.normal),
),
trailing: Icon(Icons.arrow_forward),
onTap: () {
Navigator.of(context).pop();
if (this.currentPage == "About") return;
Provider.of<DrawerStateInfo>(context).setCurrentDrawer(1);
Navigator.of(context).pushReplacement(MaterialPageRoute(
builder: (BuildContext context) => MyAboutPage()));
},
),
],
),
);
}
}
Use of Drawer in one of your pages
class MyAboutPage extends StatefulWidget {
#override
_MyAboutPageState createState() => _MyAboutPageState();
}
class _MyAboutPageState extends State<MyAboutPage> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('About Page'),
),
drawer: MyDrawer("About"),
);
}
}
In your case, you have 2 choices:
You should keep your state in your Top level widget. in your case _MyHomePageState;
Use state managers like Redux, Bloc, ScopedModel. I think ScopedModel is great for you in this case.
otherwise, you can't control the state of Drawer. cause it re-creates every moment you call the Drawer by the action button in Appbar;
This example Flutter app consist of a Drawer and two "pages" (Scaffolds) that can be navigated using the drawer:
import 'package:flutter/material.dart';
class MyTestDrawer extends StatefulWidget {
#override
_MyTestDrawerState createState() => new _MyTestDrawerState();
}
class _MyTestDrawerState extends State<MyTestDrawer> {
#override
Widget build(BuildContext context) {
return new Drawer(child: new ListView(
children: <Widget>[
new ListTile(
leading: new Icon(Icons.pregnant_woman),
title: new Text('Homepage'),
onTap: () {
Navigator.of(context).pushNamed('/');
}
),
new ListTile(
leading: new Icon(Icons.group),
title: new Text('Second page'),
onTap: () {
Navigator.of(context).pushNamed('/second');
}
),
new AboutListTile(
icon: new Icon(Icons.help)
)
]
)
);
}
}
final MyTestDrawer _drawer = new MyTestDrawer();
class FlutterTestApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'Flutter Test',
theme: new ThemeData(
primarySwatch: Colors.blue,
),
home: new HomepageWidget(),
routes: <String, WidgetBuilder> {
'/second': (BuildContext context) => new SecondPage(),
}
);
}
}
class HomepageWidget extends StatefulWidget {
#override
_HomepageWidgetState createState() => new _HomepageWidgetState();
}
class _HomepageWidgetState extends State<HomepageWidget> {
#override
Widget build(BuildContext context) {
print('build: ' + this.toString());
return new Scaffold(
drawer: _drawer,
appBar: new AppBar(title: new Text('Homepage')),
body: new Container(
child: new Center(child: new Text('Homepage'))
)
);
}
}
class SecondPage extends StatefulWidget {
#override
_SecondPageState createState() => new _SecondPageState();
}
class _SecondPageState extends State<SecondPage> {
#override
Widget build(BuildContext context) {
print('build: '+ this.toString());
return new Scaffold(
drawer: _drawer,
appBar: new AppBar(title: new Text('Second page')),
body: new Container(
child: new Center(child: new Text('Second page'))
)
);
}
}
void main() {
runApp(new FlutterTestApp());
}
I have a print debug statement in both the HomepageState and SecondPageState build() methods.
When I run this app and navigate between the Homepage and the Second page I eventually get this in my log:
build: _HomepageWidgetState#e0d4a
build: _SecondPageState#06d69
build: _SecondPageState#9c9f7
build: _SecondPageState#db373
build: _SecondPageState#4d4ca
build: _SecondPageState#15247
build: _SecondPageState#06d69
build: _SecondPageState#9c9f7
build: _SecondPageState#db373
build: _SecondPageState#4d4ca
build: _HomepageWidgetState#0d598
build: _HomepageWidgetState#3bb96
build: _HomepageWidgetState#09270
build: _HomepageWidgetState#640bb
build: _HomepageWidgetState#6385a
build: _HomepageWidgetState#ed720
build: _HomepageWidgetState#f45da
It seems a new State object is created every time I navigate between the two pages, and also its build() method keeps getting called, even if the Widget is not currently visible.
Obviously something is wrong (?) - what is going on here?
Flutter is creating a State object for each route you push, and calling its build() method every time you push a new route onto the navigator stack. You aren't currently providing a way to pop anything off the stack.
You should probably avoid specifying a drawer for SecondPage, and instead force the user to navigate back to the home page using the back button. This will prevent the Navigator history stack from becoming arbitrarily large.
Methods like Navigator.of(context).pushNamed('/'); usually create and "push" new widget. It could be the reason, why you have new state.
You can find example of Drawer in Stocks app
I'm new to flutter and I'm testing Provider and can't figure out why doing this works (by work i mean it shows a list in the appbar):
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return ChangeNotifierProvider<Data>(
builder: (context)=> Data(),
child: MaterialApp(
home: Scaffold(
appBar: AppBar(
title: CustomText(),
),
),
),
);
}
}
With a CustomText class that does practically nothing:
class CustomText extends StatelessWidget{
#override
Widget build(BuildContext context) {
return Text(Provider.of<Data>(context).texts.tostring());
}
}
But this other thing throws a - Could not find the correct Provider above this MyApp Widget - Error:
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return ChangeNotifierProvider<Data>(
builder: (context)=> Data(),
child: MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text(Provider.of<Data>(context).texts.toString()),
),
),
),
);
}
}
The Data class is :
class Data with ChangeNotifier{
List<String> _texts = ['Text 1', 'Text 2', 'Text 3', 'Text 4',];
get texts {
return _texts;
}
void deleteText(int index){
this._texts.removeAt(index);
notifyListeners();
}
void addText(String text){
this._texts.add(text);
notifyListeners();
}
}
I just can not see what is the difference or why that matters. Shouldn't that code be equivalent? Any insight will be much appreciated.
The difference is that in the CustomText case, context is from its parent widget which is MyApp, whereas in the second case, context is from MyApp's parent. Since your Provider is implemented inside MyApp, it cannot find the provider if you use MyApp's parent's context (the second case)