How to pass List _data from main() to Stateful widget (LIstView)? - dart

I want to access _data from main() async to Stateful Widget? Is it good practice to call REST Api Call in Main()?
import 'dart:async';
import 'package:http/http.dart' as http;
import 'dart:convert';
import 'package:flutter/material.dart';
Future main() async {
List _data = await makeRequest();
runApp(new MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: HomePage(),
);
}
}
Future<List> makeRequest() async {
String url = "https://jsonplaceholder.typicode.com/posts";
http.Response response = await http.get(url);
print(json.decode(response.body));
return json.decode(response.body);
}
class HomePage extends StatefulWidget {
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("JSON List"),
),
body: ListView.builder(
itemBuilder: (BuildContext context, int index) {
ListTile(
);
}
),
);
}
}

This is how it should works, I fixed your code :
class HomePage extends StatefulWidget {
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
List _data = new List();
void makeRequest() async {
String url = "https://jsonplaceholder.typicode.com/posts";
http.Response response = await http.get(url);
print(json.decode(response.body));
setState(() {
_data = json.decode(response.body) as List;
});
}
#override
void initState() {
makeRequest();
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("JSON List"),
),
body: _data.isEmpty
? Center(child: CircularProgressIndicator())
: ListView.builder(
itemCount: _data.length,
itemBuilder: (BuildContext context, int index) {
return ListTile(
title: Text(_data[index]['title']),
);
}),
);
}
}
your main call should be
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: HomePage(),
);
}
}

Related

bloc does not get initialized when using generic bloc provider

Here is the bloc (simplified):
import 'package:autobleidas_flutter/bloc/bloc_base.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:rxdart/rxdart.dart';
class LoginBloc extends BlocBase {
final FirebaseAuth _firebaseAuth = FirebaseAuth.instance;
final PublishSubject<bool> loggedIn = PublishSubject<bool>();
final PublishSubject<bool> loading = PublishSubject<bool>();
}
Here is the bloc provider:
class BlocProvider<T> extends InheritedWidget {
final T bloc;
BlocProvider({Key key, Widget child, this.bloc})
: super(key: key, child: child);
static T of<T extends BlocBase>(BuildContext context) {
final type = _typeOf<BlocProvider<T>>();
return (context.inheritFromWidgetOfExactType(type) as BlocProvider).bloc;
}
static Type _typeOf<T>() => T;
#override
bool updateShouldNotify(InheritedWidget oldWidget) {
return true;
}
}
However, in the LoginScreen I cannot access the loggedIn Subject of the bloc. Here is how LoginScreen is opened from main and the bloc is passed to it:
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData(
primarySwatch: Colors.blue,
),
localizationsDelegates: GlobalMaterialLocalizations.delegates,
supportedLocales: allTranslations.supportedLocales(),
home: BlocProvider<LoginBloc>(child: LoginScreen()), // <-------- HERE
);
}
}
Here is how I try to access it in the LoginScreen:
class _LoginScreenState extends State<LoginScreen> {
bool _isLoading = false;
#override
void didChangeDependencies() {
LoginBloc bloc = BlocProvider.of<LoginBloc>(context);
bloc.loggedIn.listen((isLoggedIn) => Navigator.of(context).push(
MaterialPageRoute(
builder: (BuildContext context) => RegistrationScreen())));
bloc.loading.listen((state) => setState(() => _isLoading = state));
super.didChangeDependencies();
}
#override
Widget build(BuildContext context) {
return Container();
}
the error:
The getter 'loggedIn' was called on null.
So why is the bloc null? How do I fix this?
In this line, BlocProvder expect a bloc.
home: BlocProvider<LoginBloc>(child: LoginScreen()),
You are not passing your bloc here.
Pass it like below:
home: BlocProvider<LoginBloc>(child: LoginScreen(),bloc: LoginBloc()),
BlocProvider<LoginBloc> means your defining a type of the bloc you are going to pass.

Using setState in separate BottomNotifcationBar class back to the main class

If I keep the bottomNotificationBar in the same class as the rest of the page, setState works properly and the buttons work properly.
If I move the bottomNotificationBar to another class, I cannot get the setState to work, because it needs to reference back to the main class. I've tried a few things, but I can't wrap my mind around this yet.
The error is:
The following assertion was thrown while handling a gesture:
setState() called in constructor:
The part that isn't working is marked near the bottom of this:
import 'package:flutter/material.dart';
void main() {
runApp(new MyApp());
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'My Title',
home: new MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => new _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
var selectedPageIndex = 0;
var pages = [ Page1(), Page2(), ];
#override
Widget build(BuildContext context) {
return new Scaffold(
body: pages[selectedPageIndex],
bottomNavigationBar:
MyClass().buildBottomNavigationBar(selectedPageIndex),
);
}
}
class MyClass {
BottomNavigationBar buildBottomNavigationBar(selectedPageIndex) {
return new BottomNavigationBar(
type: BottomNavigationBarType.fixed,
items: [
BottomNavigationBarItem(
title: Text("Page1"),
icon: Icon(Icons.account_circle),
),
BottomNavigationBarItem(
title: Text("Page2"),
icon: Icon(Icons.account_circle),
),
],
onTap: (index) {
/////////////////////////////START OF SECTION///////////////////////////
_MyHomePageState().setState(() {
selectedPageIndex = index;
});
/////////////////////////////END OF SECTION///////////////////////////
},
currentIndex: selectedPageIndex,
);
}
}
--------------EDIT:----------------
Ok, now I have the following code below, and I am getting the following 2 things:
info:
The member 'setState' can only be used within instance members of subclasses of 'package:flutter/src/widgets/framework.dart'.
exception:
The following NoSuchMethodError was thrown while handling a gesture:
The method 'setState' was called on null.
Receiver: null
Tried calling: setState(Closure: () => Null)
import 'package:flutter/material.dart';
void main() {
runApp(new MyApp());
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'My Title',
home: new MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
static void setIndex(BuildContext context, int _newIndex) {
_MyHomePageState state = context.ancestorStateOfType(TypeMatcher<_MyHomePageState>());
state.setState(() {
state.selectedPageIndex =_newIndex;
});
}
#override
_MyHomePageState createState() => new _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
var selectedPageIndex = 0;
#override
Widget build(BuildContext context) {
return new Scaffold(
body: new Container(),
bottomNavigationBar:
MyClass().buildBottomNavigationBar(context,selectedPageIndex),
);
}
}
class MyClass {
BottomNavigationBar buildBottomNavigationBar(context,selectedPageIndex) {
return new BottomNavigationBar(
type: BottomNavigationBarType.fixed,
items: [
BottomNavigationBarItem(
title: Text("Page1"),
icon: Icon(Icons.account_circle),
),
BottomNavigationBarItem(
title: Text("Page2"),
icon: Icon(Icons.account_circle),
),
],
onTap: (index) {
MyHomePage.setIndex(context, index);
},
currentIndex: selectedPageIndex,
);
}
}
What you Require is CallBAck Function from the other class. As setState has to be called on object -_MyHomePageState.
With Class Constructors we pass the initial Data & got a Callback on SetState().
import 'package:flutter/material.dart';
void main() {
runApp(new MyApp());
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'My Title',
home: new MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => new _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
var selectedPageIndex = 0;
var pages = [
Page1(),
Page2(),
];
#override
Widget build(BuildContext context) {
return new Scaffold(
body: pages[selectedPageIndex],
bottomNavigationBar: MyClass(
selectedPageIndex: selectedPageIndex,
myFunc: _myFunc,
),
);
}
void _myFunc(int index) {
setState(() {
selectedPageIndex = index;
});
}
}
class MyClass extends StatelessWidget {
MyClass({this.selectedPageIndex, this.myFunc});
final int selectedPageIndex;
final Function myFunc;
#override
Widget build(BuildContext context) {
return BottomNavigationBar(
type: BottomNavigationBarType.fixed,
items: [
BottomNavigationBarItem(
title: Text("Page1"),
icon: Icon(Icons.account_circle),
),
BottomNavigationBarItem(
title: Text("Page2"),
icon: Icon(Icons.account_circle),
),
],
onTap: (index) {
/////////////////////////////START OF SECTION///////////////////////////
myFunc(index);
/////////////////////////////END OF SECTION///////////////////////////
},
currentIndex: selectedPageIndex,
);
}
}
class Page1 extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Center(
child: Container(
child: Text('1'),
),
);
}
}
class Page2 extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Center(child: Container(child: Text('3'),));
}
}
You should modify your MyHomePage by adding a static method into it so its state can be called from anywhere:
class MyHomePage extends StatefulWidget {
static void setIndex(BuildContext context, int _newIndex) {
_MyHomePageState state = context.ancestorStateOfType(TypeMatcher<_MyHomePageState>());
state.setState(() {
state.selectedPageIndex =_newIndex;
});
}
#override
_MyHomePageState createState() => new _MyHomePageState();
}
Then when you want to change the index call:
onTap (index) {
MyHomePage.setIndex(context, index);
}

How to fix 'String is not subtype of type widget'?

I am trying to use GoogleTranslator library to translate input text, but i got an error that say type String is not subtype of type Widget
i tried to create a function that receive a text and return the translated text and used the widget on the body of the app
import 'package:flutter/material.dart';
import 'package:translator/translator.dart';
void main() => runApp(MyApp());
Widget translator(String input) {
GoogleTranslator translator = GoogleTranslator();
String translation = translator
.translate("I would buy a car, if I had money.", from: 'en', to: 'ar')
.toString();
return translation as Widget;
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('Translator'),
),
body: Center(
child: translator("Hello World"),
),
),
);
}
}
i expect the output to be in translated text in center of the screen
return translation as Widget;
should probably be
return Text(translation);
update
import 'package:flutter/material.dart';
import 'package:translator/translator.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('Translator'),
),
body: Center(
child: MyHomePage(),
),
),
);
}
}
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
final _translations = <String,String>{};
String translator(String input) {
if(_translations.containsKey(input)) {
return _translations[input];
} else {
_translate(input);
return input;
}
}
Future<void> _translate(String input) async {
GoogleTranslator translator = GoogleTranslator();
String translation = await translator
.translate("I would buy a car, if I had money.", from: 'en', to: 'ar');
setState(() => _translations[input] = translation);
}
#override
Widget build(BuildContext context) {
return Text(translator("Hello World"));
}
}

How to change a State of a StatefulWidget inside a StatelessWidget?

Just testing out flutter. The code sample below is a very simple flutter app. The problem is that I don't know how to call the setState() function inside the TestTextState class in order to change the text each time when the change button is pressed.
import 'package:flutter/material.dart';
void main() => runApp(new MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'Test app',
home: new Scaffold(
appBar: new AppBar(
title: new Text("Test"),
),
body: new Test(),
),
);
}
}
class Test extends StatelessWidget {
final TestText testText = new TestText();
void change() {
testText.text == "original" ? testText.set("changed") : testText.set("original");
}
#override
Widget build(BuildContext context) {
return new Column(
children: [
testText,
new RaisedButton(
child: new Text("change"),
onPressed: () => change(),
),
]
);
}
}
class TestText extends StatefulWidget {
String text = "original";
void set(String str) {
this.text = str;
}
#override
TestTextState createState() => new TestTextState();
}
class TestTextState extends State<TestText> {
#override
Widget build(BuildContext context) {
return new Text(this.widget.text);
}
}
I have approached this problem by initializing the _TestTextState as the final property of the TestText widget which allows to simply update the state when the change button is pressed. It seems like a simple solution but I'm not sure whether it's a good practice.
import 'package:flutter/material.dart';
void main() => runApp(new MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'Test app',
home: new Scaffold(
appBar: new AppBar(
title: new Text("Test"),
),
body: new Test(),
),
);
}
}
class Test extends StatelessWidget {
final _TestText text = new _TestText();
#override
Widget build(BuildContext context) {
return new Column(
children: [
text,
new RaisedButton(
child: new Text("change"),
onPressed: () => text.update(),
),
]
);
}
}
class TestText extends StatefulWidget {
final _TestTextState state = new _TestTextState();
void update() {
state.change();
}
#override
_TestTextState createState() => state;
}
class _TestTextState extends State<TestText> {
String text = "original";
void change() {
setState(() {
this.text = this.text == "original" ? "changed" : "original";
});
}
#override
Widget build(BuildContext context) {
return new Text(this.text);
}
}
thier is no way to do so. any how you have to convert your StatelessWidget to StatefulWidget.
Solution based on your existing code
class Test extends StatelessWidget {
final StreamController<String> streamController = StreamController<String>.broadcast();
#override
Widget build(BuildContext context) {
final TestText testText = TestText(streamController.stream);
return new Column(children: [
testText,
new RaisedButton(
child: Text("change"),
onPressed: () {
String text = testText.text == "original" ? "changed" : "original";
streamController.add(text);
},
),
]);
}
}
class TestText extends StatefulWidget {
TestText(this.stream);
final Stream<String> stream;
String text = "original";
#override
TestTextState createState() => new TestTextState();
}
class TestTextState extends State<TestText> {
#override
void initState() {
widget.stream.listen((str) {
setState(() {
widget.text = str;
});
});
super.initState();
}
#override
Widget build(BuildContext context) {
return Text(widget.text);
}
}
But it's not the best idea - to use non-final field inside Stateful Widget
P.S.
You can also use this - scoped_model

Flutter Error: MediaQuery.of() called with a context that does not contain a MediaQuery

I have been trying to get the size of the whole context view in Flutter. But every time I try I'm getting the above mentioned error.
Here's my code:
import 'package:flutter/material.dart';
void main => runApp(new MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
final size = MediaQuery.of(context).size;
return new MaterialApp(
home: new Scaffold(),
);
}
}
Note: I also tried with a StatefulWidget.
Please, help me find what I'm doing wrong here.
You need a MaterialApp or a WidgetsApp around your widget. They provide the MediaQuery. When you call .of(context) flutter will always look up the widget tree to find the widget.
You usually have this in your main.dart:
void main() => runApp(App());
class App extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Title',
theme: kThemeData,
home: HomePage(),
);
}
}
class HomePage extends StatelessWidget {
#override
Widget build(BuildContext context) {
final size = MediaQuery.of(context).size;
return Container(
child: ...,
);
}
}
What works for us is using WidgetsBinding.instance.window instead of MediaQuery - also when setting the theme of the MaterialApp:
_pixelRatio = WidgetsBinding.instance.window.devicePixelRatio;
_screenWidth = WidgetsBinding.instance.window.physicalSize.width;
_screenHeight = WidgetsBinding.instance.window.physicalSize.height;
_statusBarHeight = WidgetsBinding.instance.window.padding.top;
_bottomBarHeight = WidgetsBinding.instance.window.padding.bottom;
_textScaleFactor = WidgetsBinding.instance.window.textScaleFactor;
You can access MediaQuery when you are inside MaterialApp. The place where you are accessing the media query is not correct.
Please refer below code:
import 'package:flutter/material.dart';
class CommonThings {
static Size size;
}
void main() => runApp(new MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'MediaQuery Demo',
theme: new ThemeData(
primarySwatch: Colors.red,
),
home: new MyHomePage(),
);
}
}
class MyHomePage extends StatelessWidget {
#override
Widget build(BuildContext context) {
CommonThings.size = MediaQuery.of(context).size;
print('Width of the screen: ${CommonThings.size.width}');
return new Container();
}
}
I've purposely created a class CommonThings which has static Size so that you can use it throughout the app.
I fixed it by using the following method. First I created a new class named MyWidget and returned it in MyApp within a MaterialApp's home:. Refer code below:
import 'package:flutter/material.dart';
void main => runApp(new MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new MaterialApp(
home: new MyWidget(),
);
}
}
class MyWidget extends StatelessWidget {
#override
Widget build(BuildContext context) {
final size = MediaQuery.of(context).size;
return new MaterialApp(
home: new Scaffold(),
);
}
}
Also, declaring size as final doesn't matter. Orientation/Rotation is handled.
Solved by re-run the app(click on stop button in android studio then run again)
There is better way. Above solutions would require you to have only one screen widget or inherit all screens from parent class. But there is solution, place the media query initialization into onGenerateRoute callback function
main.dart
import 'package:flutter/material.dart';
class MyApp extends StatefulWidget {
#override
State<StatefulWidget> createState() => new MyAppState();
}
class MyAppState extends State<MyApp> {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'My Awesome App',
routes: NavigationUtils.routeList(),
onGenerateRoute: (routeSettings) =>
NavigationUtils.onGenerateRoute(routeSettings),
);
}
}
NavigationUtils.dart
import 'package:flutter/material.dart';
class NavigationUtils {
static onGenerateRoute(RouteSettings routeSettings) {
return new MaterialPageRoute(
builder: (context) {
WidgetUtils.me.init(context);
return StorageUtils.me.isLogged() ? HomeScreen() : ForkScreen();
},
settings: routeSettings,
);
}
}
WidgetUtils.dart
import 'package:flutter/material.dart';
class WidgetUtils {
MediaQueryData _mediaQueryData;
double _screenWidth;
double _screenHeight;
double _blockSizeHorizontal;
double _blockSizeVertical;
init(BuildContext context) {
_mediaQueryData = MediaQuery.of(context);
screenWidth = _mediaQueryData.size.width;
screenHeight = _mediaQueryData.size.height;
blockSizeHorizontal = screenWidth / 100;
blockSizeVertical = screenHeight / 100;
}
}
Warning: It is not copy & paste code, there are some singletons etc. but you should get the point ;)
Had the same error in
import 'screens/tasks_screen.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return TasksScreen();
}
}
I solved it by:-
import 'package:flutter/material.dart';
import 'screens/tasks_screen.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: TasksScreen(),
);
}
}
Wrap your code in a Material App widget. I also had the same issue as I forgot to use it and directly returned the scaffold.
In other words, your MediaQuery.of(context) should be inside the Material Widget.
Material app -> scaffold -> MediaQuery.of(context)
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: MyAppOne(),
);
}
}
class MyAppOne extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyAppOne>{
#override
Widget build(BuildContext context){
return Scaffold(
);
}
}
import 'package:flutter/material.dart';
void main() => runApp(App());
class App extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body:HomePage(),
),
);
}
}
class HomePage extends StatelessWidget {
#override
Widget build(BuildContext context) {
var size = MediaQuery.of(context).size.height;
return Container(
height:size/2,
color:Colors.lightBlueAccent,
);
}
}
YOU SHOULD TRY THIS I HAVE DONE IT.
I was trying to change the package then this error arise,
so make sure you complete each of the following steps
https://stackoverflow.com/a/51550358/4993045
Add MaterialApp ...
void main() {
runApp(MaterialApp(
home: HomePage(),
));
}

Resources