Error thrown while updating attribute value in flutter - flutter

I am trying to change the body of a screen in flutter based on whether there is any record in my table.
import 'package:flutter/material.dart';
import '../widgets/appDrawer.dart';
import '../widgets/emptyHomeScreen.dart';
import '../data/database.dart';
import '../widgets/filledHomeScreen.dart';
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
final dbHelper = DBProvider.db;
int count;
_MyHomePageState() {
dbHelper.getCount().then((val) => setState(() {
count = val;
}));
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('All Files'),
),
drawer: AppDrawer(),
floatingActionButton: buildFloatingActionButton(context),
body: Center(
child: count > 0 ? FilledHomeScreen() : EmptyHomeScreen(),
),
);
}
}
When I tried to run my app, error was thrown for just a fraction of second and then the app just worked fine.
How do I fix it?
The error thrown was:
════════ Exception caught by widgets library ═══════════════════════════════════════════════════════
The following NoSuchMethodError was thrown building MyHomePage(dirty, state: _MyHomePageState#4d44c):
The method '>' was called on null.
Receiver: null
Tried calling: >(0)
The code for the function getCount() is:
Future<int> getCount() async {
//database connection
final Database db = await database;
var x = await db.rawQuery('SELECT COUNT (*) from documents');
int count = Sqflite.firstIntValue(x);
return count;
}
Is there any way of doing this by converting the statefulwidget to statelesswidget?

class _MyHomePageState extends State<MyHomePage> {
final dbHelper = DBProvider.db;
int count;
_MyHomePageState() {
dbHelper.getCount().then((val) => setState(() {
count = val;
}));
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('All Scans'),
),
drawer: AppDrawer(),
floatingActionButton: buildFloatingActionButton(context),
body: Center(
child: count != null && count > 0 ? FilledHomeScreen() : EmptyHomeScreen(),
),
);
}
}

Related

The method '[ ]' was called on null when calling api

i want to get data from open weather api, and i keep getting this:
NoSuchMethodError: The method '[]' was called on null.Reciever: null
tried calling:
is it a problem with the api or the code?the editor don't show any errors please help
my code:
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';
void main() {
runApp(MaterialApp(
title: 'Weather',
home: Weather(),
));
}
String api_key = 'the key';
Future<List> fetchData() async {
var result = await http.get(Uri.http('http://api.openweathermap.org',
'weather?lat=15.53804515&lon=32.52674103&appid=$api_key&units=metric'));
return json.decode(result.body);
}
class Weather extends StatefulWidget {
#override
_WeatherState createState() => _WeatherState();
}
class _WeatherState extends State<Weather> {
Color bgcolor;
var data;
#override
void initState() {
super.initState();
fetchData().then((value) {
setState() {
data = value;
}
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.blue,
appBar: AppBar(
title: Text('Weather App'),
centerTitle: true,
),
body: Center(
child: Column(children: [
Text(data['main']['temp']),
Text(data['weather']['description']),
Text(data['name'])
]),
));
}
}
fetchData is asynchronous, meaning it will take some time to execute and fetch that weather data. Until then, the variable "data" is uninitialized and thus null. Try adding a check for if data is null, then return a loading page or CircularProgressIndicator.
Just add a simple null check -
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';
void main() {
runApp(MaterialApp(
title: 'Weather',
home: Weather(),
));
}
String api_key = 'the key';
Future<List> fetchData() async {
var result = await http.get(Uri.http('http://api.openweathermap.org',
'weather?lat=15.53804515&lon=32.52674103&appid=$api_key&units=metric'));
return json.decode(result.body);
}
class Weather extends StatefulWidget {
#override
_WeatherState createState() => _WeatherState();
}
class _WeatherState extends State<Weather> {
Color bgcolor;
var data;
#override
void initState() {
super.initState();
fetchData().then((value) {
setState() {
data = value;
}
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.blue,
appBar: AppBar(
title: Text('Weather App'),
centerTitle: true,
),
body: Center(
child: data == null
? CircularProgressIndicator()
: Column(children: [
Text(data['main']['temp']),
Text(data['weather']['description']),
Text(data['name'])
]),
));
}
}

Flutter 'The setter 'task= ' was called on null

everyone!
I'm learning Flutter / Dart and keep having issues with instantiation and initialization.
Currently I'm writing a reorderable list but for some reason, I can't populate the list because my instance has some issue... (I think the issue is at these lines below)
List<String> tasks = [];
MyTask t;
tasks = [ t.task = 'Buy ice cream', t.task = 'Learn Flutter', t.task = 'Read books' ];)
Can you check it and give me a clue?
Thanks in advance. Any tip to seek docs is welcome!
Exception:
════════ Exception caught by widgets library ═══════════════════════════════════════════════════════
The following NoSuchMethodError was thrown building _BodyBuilder:
The setter 'task=' was called on null.
Receiver: null
Tried calling: task="Buy ice cream"
Relevant code:
import 'package:flutter/material.dart';
import './bottomNavigationBar.dart';
import './ViewFeed.dart';
import './ViewNewTodo.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
//home: MyScaffold(),
initialRoute: '/',
routes: {
'/':(context) => MyFeed(),
'/toDo':(context) => MyScaffold(),
'/newToDo':(context) => MyNewTodo(),
},
);
}
}
class MyScaffold extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('My ToDoS'),
),
body: MyReorderableTaskList(),
floatingActionButton: MyFloatingActionButtonNewTodo() ,
bottomNavigationBar: MyBottomNavigationBar(),
);
}
}
//Reorderable list elements
//FloatingActionButton
class MyFloatingActionButtonNewTodo extends StatelessWidget {
#override
Widget build(BuildContext context) {
return FloatingActionButton(
child: Icon(Icons.add),
tooltip: 'Idea',
onPressed: ()=>{ Navigator.pushNamed(context, '/newToDo') },
);
}
}
class MyTask{
String task;
MyTask(this.task);
}
//ReorderableListView implementation
class MyReorderableTaskList extends StatefulWidget {
#override
_MyReorderableTaskListState createState() => _MyReorderableTaskListState();
}
class _MyReorderableTaskListState extends State<MyReorderableTaskList> {
List<String> tasks = [];
MyTask t;
void initState(){
tasks = [ t.task = 'Buy ice cream', t.task = 'Learn Flutter', t.task = 'Read books' ];
super.initState();
}
#override
Widget build(BuildContext context) {
return ReorderableListView(
onReorder: _onReorder,
children: List.generate(
tasks.length,
(index){ return MyListView(index, Key('$index'), tasks ); }
),
);
}
void _onReorder(int oldIndex, int newIndex){
setState(() {
if(newIndex > oldIndex) { newIndex -= 1; }
final String item = tasks.removeAt(oldIndex);
tasks.insert(newIndex, item);
});
}
}
class MyListView extends StatefulWidget {
final int index;
final Key key;
final List<String> listTasks;
MyListView(this.index, this.key, this.listTasks);
#override
_MyListViewState createState() => _MyListViewState();
}
class _MyListViewState extends State<MyListView> {
#override
Widget build(BuildContext context) {
return Card(
margin: EdgeInsets.all(6),
child: InkWell(
splashColor: Colors.blue,
onTap: ()=>{ MaterialState.focused },
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
ListTile(
title: Text('Tarea: ${widget.listTasks[widget.index]} | ${widget.index}',),
),
],
),
),
);
}
}
MyTask t; is null. t isn't actually anything yet. You must create a new object MyTask() and assign it to t
Change it to
MyTask t = new MyTask("");
Edit: More thorough explanation
You can't just declare MyTask t; and then try to use t, because you have not yet said what t is. That's like declaring int a; and then trying to do print(a + 5). You can't do that, because you have not yet assigned a value to a. Similarly, MyTask t; means you have created a variable named t of type MyTask. In other words, you have declared a variable t. But you must still initialize it, in other words, assign t a value of type MyTask, otherwise the value of t will be null.
So to summarize,
MyTask t;
t.task = "Hello";
will not work, because it's like doing
int a;
int b = a + 5;
You can't add 5 to a, because although you have declared the variable a, you have not yet initialized it, so it does not yet have a value.
Likewise, you can't access the .task property of t, because you haven't said what t is. t is just null.
So you must initialize t by instantiating a new MyTask object-
MyTask t = new MyTask("");
The "" is required inside the parentheses because your MyTask class constructor requires a parameter.
class MyTask{
String task;
MyTask(this.task); // Parameter required to assign to the String task
}
This might help you.

Flutter : No SuchMethodError when I called Stateful Widget

I'm new to flutter. I want to make an app that shows a page selected by BottomNavigationBar.
But when I'm trying to run the app, it throws an Exception. The following is Error Log.
════════ Exception caught by widgets library
The following NoSuchMethodError was thrown building Builder:
The method '_debugTypesAreRight' was called on null.
Receiver: null
Tried calling: _debugTypesAreRight(Instance of 'MainPages')
The relevant error-causing widget was:
MaterialApp file:///C:/Users/jango/AndroidStudioProjects/study_and_statistic/lib/main.dart:49:14
When the exception was thrown, this was the stack:
0 Object.noSuchMethod (dart:core-patch/object_patch.dart:53:5)
1 new StatefulElement.<anonymous closure> (package:flutter/src/widgets/framework.dart:4309:19)
2 new StatefulElement (package:flutter/src/widgets/framework.dart:4320:6)
3 StatefulWidget.createElement (package:flutter/src/widgets/framework.dart:809:38)
4 Element.inflateWidget (package:flutter/src/widgets/framework.dart:3189:40)
and my code is here
main.dart
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class AppConfig {
static double width;
static double height;
static double blockSize;
static double blockSizeVertical;
static double statusBarHeight;
static double getAppbarHeight(){
double ratioHeight = blockSizeVertical*9;
return (ratioHeight>60)? 60 : ratioHeight;
}
static double getGap(){
double ratioGap = width/20;
return (ratioGap>30)? 30 : ratioGap;
}
static double getFontsize_content(){
double ratioSize = (blockSize>blockSizeVertical)?blockSizeVertical*6:blockSize*6;
return (ratioSize > 18)? 18: ratioSize;
}
static double getFontsize_appBar(){
double ratioSize = (blockSize>blockSizeVertical)?blockSizeVertical*7:blockSize*7;
return (ratioSize > 20)? 20: ratioSize;
}
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return GestureDetector(
onTap: (){
FocusScope.of(context).unfocus();
},
child: MaterialApp(
title: 'STUDY',
theme: ThemeData(
fontFamily: 'NanumBarunGothic',
primaryColor: Color(0XFF5dc19b),
),
home: MainPages() //Here is the problem, maybe..
)
);
}
}
PreferredSize DailyAppBar(){
//My customAppBar
//AppConfig used here
}
class SubjectListTile extends StatelessWidget{
//My custom ListTile
//AppConfig used here
}
class SubjectList extends StatefulWidget{
#override
State createState() => SubjectListState();
}
class SubjectListState extends State<SubjectList>{
//My custom Listview
}
class MainPages extends StatefulWidget{
const MainPages({ Key key }) : super(key: key);
#override
_MainPagesState createState() {
_MainPagesState();
}
}
class _MainPagesState extends State<MainPages>{
int _currentIndex = 0;
final List<Widget> pages = [
SubjectList(),
StudyPage(),
StaticPage(),
];
void init_AppConfig(BuildContext context){
AppConfig.width = MediaQuery.of(context).size.width;
AppConfig.height = MediaQuery.of(context).size.height;
AppConfig.blockSize = AppConfig.width / 100;
AppConfig.blockSizeVertical = AppConfig.height / 100;
AppConfig.statusBarHeight = MediaQuery.of(context).padding.top;
double width = AppConfig.width;
double height = AppConfig.height;
print('width: $width');
print('height: $height');
}
void _onItemTapped(int index){
setState((){
_currentIndex = index;
});
}
#override
Widget build(BuildContext context) {
init_AppConfig(context);
return Scaffold(
appBar: DailyAppBar(),
body : pages[_currentIndex],
bottomNavigationBar: BottomNavigationBar(
currentIndex: _currentIndex,
onTap: _onItemTapped,
items: <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(Icons.check_box),
title: Text('오늘의 공부'),
),
BottomNavigationBarItem(
icon: Icon(Icons.chrome_reader_mode),
title: Text('집중모드'),
),
BottomNavigationBarItem(
icon: Icon(Icons.show_chart),
title: Text('기록'),
),
],
),
);
}
}
class StaticPage extends StatelessWidget{ //Not impleted yet
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(child:Text("StaticPage")),
);
}
}
class StudyPage extends StatelessWidget{ //Not impleted yet
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(child:Text("StudyPage")),
);
}
}
In MyApp, MainPages() is called as home of MaterialApp. At that time, it throws an Exception.
In MainPagesState class, build() function initializes App Configuration first.
And then it builds Scaffold Widget, which includes DailyAppBar(my custom Appbar), pages[_currentIndex], bottomNavigationBar. Daily AppBar and pages[0] use AppConfig Data.
Is there a mistake when using init_appConfig or bottomNavigationBar?
Appconfig, SubjectListTile, SubjectList and State, DailyAppBar worked well when I put SubjectList() in body of Scaffold directly.
You have missed the return statement.
#override
_MainPagesState createState() {
return _MainPagesState();
}
or just use arrow function
#override
_MainPagesState createState() => _MainPagesState();

How to show errors from ChangeNotifier using Provider in Flutter

I'm trying to find the best way to show errors from a Change Notifier Model with Provider through a Snackbar.
Is there any built-in way or any advice you could help me with?
I found this way that is working but I don't know if it's correct.
Suppose I have a simple Page where I want to display a list of objects and a Model where I retrieve those objects from api. In case of error I notify an error String and I would like to display that error with a SnackBar.
page.dart
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
class Page extends StatefulWidget {
Page({Key key}) : super(key: key);
#override
_PageState createState() => _PageState();
}
class _PageState extends State< Page > {
#override
void initState(){
super.initState();
Provider.of<Model>(context, listen: false).load();
}
#override
void didChangeDependencies() {
super.didChangeDependencies();
Provider.of< Model >(context, listen: false).addListener(_listenForErrors);
}
#override
Widget build(BuildContext context){
super.build(context);
return Scaffold(
appBar: AppBar(),
body: Consumer<Model>(
builder: (context, model, child){
if(model.elements != null){
...list
}
else return LoadingWidget();
}
)
)
);
}
void _listenForErrors(){
final error = Provider.of<Model>(context, listen: false).error;
if (error != null) {
Scaffold.of(context)
..hideCurrentSnackBar()
..showSnackBar(
SnackBar(
backgroundColor: Colors.red[600],
content: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Icon(Icons.error),
Expanded(child: Padding( padding:EdgeInsets.only(left:16), child:Text(error) )),
],
),
),
);
}
}
#override
void dispose() {
Provider.of<PushNotificationModel>(context, listen: false).removeListener(_listenForErrors);
super.dispose();
}
}
page_model.dart
import 'package:flutter/foundation.dart';
class BrickModel extends ChangeNotifier {
List<String> _elements;
List<String> get elements => _elements;
String _error;
String get error => _error;
Future<void> load() async {
try{
final elements = await someApiCall();
_elements = [..._elements, ...elements];
}
catch(e) {
_error = e.toString();
}
finally {
notifyListeners();
}
}
}
Thank you
Edit 2022
I ported (and reworked) this package also for river pod if anyone is interested
https://pub.dev/packages/riverpod_messages/versions/1.0.0
EDIT 2020-06-05
I developed a slightly better approach to afford this kink of situations.
It can be found at This repo on github so you can see the implementation there, or use this package putting in your pubspec.yaml
provider_utilities:
git:
url: https://github.com/quantosapplications/flutter_provider_utilities.git
So when you need to present messages to the view you can:
extend your ChangeNotifier with MessageNotifierMixin that gives your ChangeNotifier two properties, error and info, and two methods, notifyError() and notifyInfo().
Wrap your Scaffold with a MessageListener that will present a Snackbar when it gets called notifyError() or NotifyInfo()
I'll give you an example:
ChangeNotifier
import 'package:flutter/material.dart';
import 'package:provider_utilities/provider_utilities.dart';
class MyNotifier extends ChangeNotifier with MessageNotifierMixin {
List<String> _properties = [];
List<String> get properties => _properties;
Future<void> load() async {
try {
/// Do some network calls or something else
await Future.delayed(Duration(seconds: 1), (){
_properties = ["Item 1", "Item 2", "Item 3"];
notifyInfo('Successfully called load() method');
});
}
catch(e) {
notifyError('Error calling load() method');
}
}
}
View
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:provider_utilities/provider_utilities.dart';
import 'notifier.dart';
class View extends StatefulWidget {
View({Key key}) : super(key: key);
#override
_ViewState createState() => _ViewState();
}
class _ViewState extends State<View> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: MessageListener<MyNotifier>(
child: Selector<MyNotifier, List<String>>(
selector: (ctx, model) => model.properties,
builder: (ctx, properties, child) => ListView.builder(
itemCount: properties.length,
itemBuilder: (ctx, index) => ListTile(
title: Text(properties[index])
),
),
)
)
);
}
}
OLD ANSWER
thank you.
Maybe I found a simpler way to handle this, using the powerful property "child" of Consumer.
With a custom stateless widget (I called it ErrorListener but it can be changed :))
class ErrorListener<T extends ErrorNotifierMixin> extends StatelessWidget {
final Widget child;
const ErrorListener({Key key, #required this.child}) : super(key: key);
#override
Widget build(BuildContext context) {
return Consumer<T>(
builder: (context, model, child){
//here we listen for errors
if (model.error != null) {
WidgetsBinding.instance.addPostFrameCallback((_){
_handleError(context, model); });
}
// here we return child!
return child;
},
child: child
);
}
// this method will be called anytime an error occurs
// it shows a snackbar but it could do anything you want
void _handleError(BuildContext context, T model) {
Scaffold.of(context)
..hideCurrentSnackBar()
..showSnackBar(
SnackBar(
backgroundColor: Colors.red[600],
content: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Icon(Icons.error),
Expanded(child: Padding( padding:EdgeInsets.only(left:16), child:Text(model.error) )),
],
),
),
);
// this will clear the error on model because it has been handled
model.clearError();
}
}
This widget must be put under a scaffold if you want to use a snackbar.
I use a mixin here to be sure that model has a error property and a clarError() method.
mixin ErrorNotifierMixin on ChangeNotifier {
String _error;
String get error => _error;
void notifyError(dynamic error) {
_error = error.toString();
notifyListeners();
}
void clearError() {
_error = null;
}
}
So for example we can use this way
class _PageState extends State<Page> {
// ...
#override
Widget build(BuildContext context) =>
ChangeNotifierProvider(
builder: (context) => MyModel(),
child: Scaffold(
body: ErrorListener<MyModel>(
child: MyBody()
)
)
);
}
You can create a custom StatelessWidget to launch the snackbar when the view model changes. For example:
class SnackBarLauncher extends StatelessWidget {
final String error;
const SnackBarLauncher(
{Key key, #required this.error})
: super(key: key);
#override
Widget build(BuildContext context) {
if (error != null) {
WidgetsBinding.instance.addPostFrameCallback(
(_) => _displaySnackBar(context, error: error));
}
// Placeholder container widget
return Container();
}
void _displaySnackBar(BuildContext context, {#required String error}) {
final snackBar = SnackBar(content: Text(error));
Scaffold.of(context).hideCurrentSnackBar();
Scaffold.of(context).showSnackBar(snackBar);
}
}
We can only display the snackbar once all widgets are built, that's why we have the WidgetsBinding.instance.addPostFrameCallback() call above.
Now we can add SnackBarLauncher to our screen:
class SomeScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(
'Title',
),
),
body: Stack(
children: [
// Other widgets here...
Consumer<EmailLoginScreenModel>(
builder: (context, model, child) =>
SnackBarLauncher(error: model.error),
),
],
),
);
}
}

Does stream builder build my widgets again and again without any change in stream?

Stream builder in Flutter is getting recalled. I am not sure why. I believe the problem might be that i have a bloc provider in stream builder. My stream dataBloc.dataStream is not changing, to cause the streambuilder to build again. Not sure, what i am doing wrong. Does stream builder build my widgets again and again without any change in stream. Obviously that's not true! Right?
Widget build(context) {
final DataBloc dataBloc = DataBlocProvider.of(context);
print("dropdown build called again");
// this doesn't print recursively so this is perfect.
// So my build is not getting called again.
return StreamBuilder(
stream: dataBloc.dataStream,
builder: (context, snapshot) {
//ToDo remove prints
print("dropdown ${snapshot.data}");
// there is no change in snapshot.data, however print is getting called recursively. This is bad and wrong
// so my stream builder is getting called again, and this is wrong
String key = dataElement.conditionalField;
String _valueArray = dataElement.conditionalValues.toString();
String conditionalValue =
_valueArray.substring(1, _valueArray.length - 1);
Map<String, String> dataMap = snapshot.hasData ? snapshot.data : {};
bool isVisible = true;
if (key != "" &&
dataMap.containsKey(key) &&
dataMap[key] == conditionalValue.toString()) {
isVisible = true;
} else if (key != "") {
isVisible = false;
}
return Visibility(
child: BlocDropDownProvider(
fieldName: dataElement.key,
dataBloc: dataBloc,
child: Card(
color: Colors.grey[100],
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
label,
new Container(
height: 8.0,
),
dropDown,
],
),
),
),
visible: isVisible? true:false,
);
output on console is :
I/flutter (14422): dropdown {idnumber: 10}
I/flutter (14422): dropdown {idnumber: 10}
I can't really replicate this issue with 1:1 accuracy base from the given details. What I encountered similarly is that build within StreamBuilder is being called again on ConnectionState changes.
Here's a minimal repro using StreamBuilder sending a HTTP request. The HTTP request sample here is based from this Flutter Networking guide.
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
final streamController = StreamController();
#override
void initState() {
super.initState();
fetchAlbum().then((response) => streamController.add(response));
}
#override
void dispose() {
super.dispose();
streamController.close();
}
#override
Widget build(BuildContext context) {
debugPrint('build');
return StreamBuilder(
stream: streamController.stream,
builder: (BuildContext context, AsyncSnapshot<dynamic> snapshot) {
debugPrint('Stream $snapshot');
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: snapshot.hasData
? Text('Album ${snapshot.data.title}')
: Text('Waiting...'),
),
);
},
);
}
Future<Album> fetchAlbum() async {
final response =
await http.get('https://jsonplaceholder.typicode.com/albums/1');
if (response.statusCode == 200) {
// If the server did return a 200 OK response,
// then parse the JSON.
return Album.fromJson(jsonDecode(response.body));
} else {
// If the server did not return a 200 OK response,
// then throw an exception.
throw Exception('Failed to load album');
}
}
}
class Album {
final int userId;
final int id;
final String title;
Album({this.userId, this.id, this.title});
factory Album.fromJson(Map<String, dynamic> json) {
return Album(
userId: json['userId'],
id: json['id'],
title: json['title'],
);
}
}
Then again, I suggest not being too concerned with build costs as long as the Widgets inside the build is manageable. More details about Flutter best practices are discussed in this doc.