Future Builder to make Sliver Lists - dart

I am trying to generate a dynamic list of slivers from a GET request. But I am having trouble, it seems the response data is null. Here is my code:
import 'package:flutter/material.dart';
import 'boardSummary.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';
import 'dart:async';
import 'package:flutter/foundation.dart';
class HomepageBody extends StatefulWidget{
#override
State<StatefulWidget> createState() {
return HomepageBodyState();
}
}
class HomepageBodyState extends State <HomepageBody> {
#override
Widget build(BuildContext context) {
return new Expanded(
child: new Container(
color: new Color(0xFF736AB7),
child: new FutureBuilder <List<Post>>(
future: fetchPost(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.hasError) print(snapshot.error);
else
return jobscroll(context, snapshot);
//: Center(child: CircularProgressIndicator());
}
),
),
);
}
}
Future<List<Post>>fetchPost() async {
final response = await http.get('https://jsonplaceholder.typicode.com/posts?userId=1');
return compute(parsePosts, response.body);
}
List<Post> parsePosts(String responseBody){
final parsed = json.decode(responseBody).cast<Map<String, dynamic>>();
return parsed.map<Post>((json)=>Post.fromJson(json)).toList();
}
class Post {
final String userId;
final String hashtag;
final String price;
final String description;
Post({this.userId, this.hashtag, this.price, this.description});
factory Post.fromJson(Map<String, dynamic> json) {
return Post(
userId: json['userId'],
hashtag: json['id'],
price: json['title'],
description: json['body'],
);
}
}
Widget jobscroll(BuildContext context, AsyncSnapshot snapshot) {
List data = snapshot.data;
return CustomScrollView(
scrollDirection: Axis.vertical,
shrinkWrap: false,
slivers: <Widget>[new SliverPadding(
padding: const EdgeInsets.symmetric(vertical: 24.0),
sliver: new SliverList(
delegate: new SliverChildBuilderDelegate(
(context, index) => new BoardSummary(data[index]),
childCount: data.length,
),
),
),
],
);
}
BoardSummary is a stateless widget class that just takes creates a "card" using the properties on each "Post". It takes in a object of type "Post."
The console spit out a lot of errors but this was the last one that seemed meaningful it also appeared in my emulator:
I/flutter (18882): Another exception was thrown: NoSuchMethodError: The getter 'length' was called on null.
EDIT Here's also the first few lines from my slack trace:
E/flutter (13964): type 'int' is not a subtype of type 'String'
E/flutter (13964): #0 new Post.fromJson (file:///home/daniel/Desktop/testapp/lib/ui/homePageBody.dart:76:19)
E/flutter (13964): #1 parsePosts.<anonymous closure> (file:///home/daniel/Desktop/testapp/lib/ui/homePageBody.dart:60:40)
E/flutter (13964): #2 MappedListIterable.elementAt (dart:_internal/iterable.dart:414:29)
E/flutter (13964): #3 ListIterable.toList (dart:_internal/iterable.dart:219:19)
E/flutter (13964): #4 parsePosts (file:///home/daniel/Desktop/testapp/lib/ui/homePageBody.dart:60:56)
E/flutter (13964): #5 _IsolateConfiguration.apply (package:flutter/src/foundation/isolates.dart:88:16)
E/flutter (13964): #6 _spawn.<anonymous closure> (package:flutter/src/foundation/isolates.dart:96:30)
What should I do?
Edit casting this piece of code to string made it work, just have overflow to fix. If there is a better solution feel free to share!
return Post(
userId: json['userId'].toString(),
hashtag: json['id'].toString(),
price: json['title'].toString(),
description: json['body'].toString(),
);

FutureBuilder builds immediately even when the value is not yet available, because build() is sync and can't be delayed.
The FutureBuilder example shows how to check if the value is already available (default: ... and not snapshot.hasError):
new FutureBuilder<String>(
future: _calculation, // a Future<String> or null
builder: (BuildContext context, AsyncSnapshot<String> snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.none: return new Text('Press button to start');
case ConnectionState.waiting: return new Text('Awaiting result...');
default:
if (snapshot.hasError)
return new Text('Error: ${snapshot.error}');
else
return new Text('Result: ${snapshot.data}');
}
},
)

Related

Flutter scoped_model not find when try to access with Navigator page

I am using flutter scoped_model, When we try to access ScopedModel from child Widget which able to access without error.
But same code was not working with when i try to access it from Widget which load using Navigator.push, it gives error Error: Could not find the correct ScopedModel.
PageModel declare at top page.
import 'package:flutter/material.dart';
import 'package:scoped_model/scoped_model.dart';
class PageModel extends Model {
String title;
static PageModel of(BuildContext context) =>
ScopedModel.of<PageModel>(context);
loadTitle() {
title = 'Old Title ';
}
updateTitle() {
title = 'New Title';
notifyListeners();
}
}
class HomePage extends StatefulWidget {
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
final PageModel model = PageModel();
#override
Widget build(BuildContext context) {
return ScopedModel<PageModel>(
model: model,
child: Scaffold(
appBar: AppBar(
title: Text('Home'),
),
body: HomePageBody(),
),
);
}
}
class HomePageBody extends StatefulWidget {
#override
_HomePageBodyState createState() => _HomePageBodyState();
}
class _HomePageBodyState extends State<HomePageBody> {
#override
void initState() {
PageModel.of(context).loadTitle();
super.initState();
}
#override
Widget build(BuildContext context) {
return ScopedModelDescendant<PageModel>(
builder: (BuildContext context, child, PageModel model) {
return Column(
children: <Widget>[
Text(model.title),
RaisedButton(
child: Text('Edit'),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => DetailPage(), fullscreenDialog: true),
);
},
),
],
);
});
}
}
class DetailPage extends StatefulWidget {
#override
_DetailPageState createState() => _DetailPageState();
}
class _DetailPageState extends State<DetailPage> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Edit Page'),
),
body: RaisedButton(
child: Text('Update'),
onPressed: () {
PageModel.of(context).updateTitle();
},
),
);
}
}
From DetailPage when we call PageModel.of(context).updateTitle(); following error coming,
/flutter (26710): ══╡ EXCEPTION CAUGHT BY GESTURE ╞═══════════════════════════════════════════════════════════════════
I/flutter (26710): The following ScopedModelError was thrown while handling a gesture:
I/flutter (26710): Error: Could not find the correct ScopedModel.
I/flutter (26710):
I/flutter (26710): To fix, please:
I/flutter (26710):
I/flutter (26710): * Provide types to ScopedModel<MyModel>
I/flutter (26710): * Provide types to ScopedModelDescendant<MyModel>
I/flutter (26710): * Provide types to ScopedModel.of<MyModel>()
I/flutter (26710): * Always use package imports. Ex: `import 'package:my_app/my_model.dart';
I/flutter (26710):
I/flutter (26710): If none of these solutions work, please file a bug at:
I/flutter (26710): https://github.com/brianegan/scoped_model/issues/new
What you’re doing is trying to find a PageModel of that context which there is none, since you haven’t created any for that specific widget context.
What you want to wrap your RaisedButton in a ScopedModelDescendant<PageModel> and update your model by using the model.updateTitle() instead.
That will look for the closest PageModel ancestor in the tree.

Flutter: showDialog: build function returned null

I have a StatefulWidget. Then when I click a button, it shows an alert dialog. When I implement:
onTap: () {
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text("Hello"),
);
}
}
Everything works fine. But when I transfered the things inside the builder to a different StatefulWidget, then this error occurs:
A build function returned null.
I/flutter ( 3647): The offending widget is: Builder
I/flutter ( 3647): Build functions must never return null. To return an empty space that causes the building widget to
I/flutter ( 3647): fill available room, return "new Container()". To return an empty space that takes as little room as
I/flutter ( 3647): possible, return "new Container(width: 0.0, height: 0.0)".
Here is the code:
Here is the calling StatefulWidget:
onTap: () {
showDialog(
context: context,
builder: (BuildContext context) {
LastVacDialog(
currentDose: currDose,
currentDate: currDate,
currentIndex: i,
setValue: changeDoseValueAndDate,
);
},
);
},
Here is the new StatefulWidget:
class LastVacDialog extends StatefulWidget {
LastVacDialog({
this.currentDose,
this.currentDate,
this.setValue,
this.currentIndex,
});
final int currentDose;
final DateTime currentDate;
final void Function(int, DateTime, int) setValue;
final currentIndex;
#override
LastVacDialogState createState() => new LastVacDialogState();
}
class LastVacDialogState extends State<LastVacDialog> {
int _dose;
DateTime _today;
#override
Widget build(BuildContext context) {
return AlertDialog(
title: Text("Last Dose"),
);
}
}
Is there something wrong with my code? I just omitted some variables for simplicity.
Add the Word Return in Front of - LastVacDialog
builder: (BuildContext context) {
return LastVacDialog(
...
As Error is Stating Build function must never return null. So return your LastVacDialog Widget by adding return in front of it.

type '_InternalLinkedHashMap<String, dynamic>' is not a subtype of type 'List<Map<String, dynamic>>'

I'm little bit stuck on some situation.
1) type '_InternalLinkedHashMap<String, dynamic>' is not a subtype of type 'List<Map<String, dynamic>>'
My code.
List<Map<String, dynamic>> arrayOfProductList = List<Map<String, dynamic>>();
Calling APIs
Future<Map<String, dynamic>> _callWebServiceForGetProductList() async{
String strURL = 'http:xxxxxx/productlist.php';
Map<String, String> headerDic = {"Authorization": "ff624bc580ab48a3910d4352dxxx712"};
Map<String, String> paramDic = {"page": "1", "search": "", "term_id": ""};
http.Response httpRes = await http.post(strURL, headers: headerDic, body: paramDic);
Map<String, dynamic> responseDic = json.decode(httpRes.body);
return responseDic;
}
FutureBuilder Widget and stuff
Expanded(
child: Container(
child: FutureBuilder(
future: _callWebServiceForGetProductList(),
builder: (BuildContext context, AsyncSnapshot snap) {
if(snap.hasData) {
Map<String, dynamic> dicOfData = snap.data;
arrayOfProductList = dicOfData["data"] as List<Map<String, dynamic>>;
print(arrayOfProductList);
_setupProductListView();
}
else if(snap.hasError) {
showDialog(context: context, builder:(_) => AlertDialog(
content: Text("Error ${snap.hasError.toString()}"),
title: Text("Error"),
));
}
return Center(child: CircularProgressIndicator());
}
),
),
)
I'm googling for last 2 hours but didn't get any result. Where i'm going wrong? Guide me.
The cast attempt on dicOfData["data"] as List<Map<String, dynamic>> seems to have caused the type mismatch error you received. If you can provide more details on the data the snapshot contains, this can help us identify why it causes the error.

Flutter app error - getter 'value' was called on null

I'm building one app where on top of Camera view I need to show something. Here is my code.
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:camera/camera.dart';
List<CameraDescription> cameras;
void main() async {
runApp(new MaterialApp(
home: new CameraApp(),
));
}
class CameraApp extends StatefulWidget {
#override
_CameraAppState createState() => new _CameraAppState();
}
class _CameraAppState extends State<CameraApp> {
CameraController controller;
var cameras;
bool cameraGot = false;
Future<Null> getCamera() async {
cameras = await availableCameras();
setState(() {
this.cameras = cameras;
this.cameraGot = true;
});
}
#override
void initState() {
getCamera();
super.initState();
if(this.cameraGot) {
controller = new CameraController(this.cameras[0], ResolutionPreset.medium);
controller.initialize().then((_) {
if (!mounted) {
return;
}
setState(() {});
});
}
}
#override
void dispose() {
controller?.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
// camera widget
Widget cameraView = new Container(
child: new Row(children: [
new Expanded(
child: new Column(
children: <Widget>[
new AspectRatio(
aspectRatio:
controller.value.aspectRatio,
child: new CameraPreview(controller)
)
]
),
)
])
);
return new Scaffold(
body: new Stack(
children: <Widget>[
!this.controller.value.initialized ? new Container() : cameraView,
// ---On top of Camera view add one mroe widget---
],
),
);
}
}
Whenever I'm building the app I'm getting following error...
I/flutter ( 6911): ══╡ EXCEPTION CAUGHT BY WIDGETS LIBRARY ╞═══════════════════════════════════════════════════════════
I/flutter ( 6911): The following NoSuchMethodError was thrown building CameraApp(dirty, state: _CameraAppState#b6034):
I/flutter ( 6911): The getter 'value' was called on null.
I/flutter ( 6911): Receiver: null
I/flutter ( 6911): Tried calling: value
Can't able to fig. out what's the error.
If you have created and assigned value to the variable and still it shows getter 'value' was called on null,
try to Run or Restart your app instead of Hot Reload. Because Hot Reload will not call initstate() (where variables assign their values) which will be only called by Restarting the app.
In the initState, you don't always instantiate a controller.
Which means it can be null. Therefore inside the build method, you need to check null to not crash
(!this.controller?.value?.initialized ?? false) ? new Container() : cameraView ,
aspectRatio: (this.controller?.value?.aspectRatio) ?? false
aspectRatio is a double value and you assigned it to false when it is null. Give it a double value.
I had this problem before and putting a height into the parent widget of AspectRatio fixed it.
giving height to widget which is throwing error solved my issue
I solved the problem this way:
children: <Widget>[
(controller== null)
? Container()
: controller.value.Initialized
? Container(child: CameraPreview(controller))
: Container(),
],
Most of the time. HOT RESTART (R) will fix this error.

Flutter: Inherited Widget and Routes

I want to have an inherited widget at the root of my application, which will contain my data providers, which I would use throughout the app. So I have this inherited widget, but every time I try to load it I get this The getter 'data' was called on null and I can't figure out why.
So here's my main.dart:
void main() => runApp(new MatAppRoot());
class MatAppRoot extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'MyCoolApp',
routes: <String, WidgetBuilder>{
'Login': (BuildContext context) => new LoginPage(),
'Cool': (BuildContext context) => new CoolPage(),
},
home: new CoolApp(),
);
}
}
class CoolAppextends StatefulWidget {
final Widget child;
CoolApp({this.child});
#override
CoolAppState createState() => new CoolAppState();
static CoolAppState of(BuildContext context) {
return (context.inheritFromWidgetOfExactType(CoolInherit) as CoolInherit).data;
}
}
class CoolAppState extends State<CoolApp> {
String randomString = 'AYEEAS!!!';
#override
void initState() { super.initState();
Navigator.of(context).pushNamedAndRemoveUntil('Login', (Route<dynamic> route) => false);
}
#override
Widget build(BuildContext context) {
return new CoolInherit(
data: this,
child: new LoginPage(),
);
}
}
class CoolInherit extends InheritedWidget {
final CoolAppState data;
CoolInherit({
Key key,
this.data,
Widget child,
}): super(
key: key,
child: child
);
#override
bool updateShouldNotify(CoolInherit old) {
return true;
}
}
then my LoginPage basically redirects after the login like this:
if (logInSuccessful) {
Navigator.of(context).pushNamedAndRemoveUntil('Cool', (Route<dynamic> route) => false);
}
In my Cool page I try to load another page when clicking a button like this:
viewCoolDetails() {
Navigator.push(
context,
new MaterialPageRoute(builder: (context) => new CoolDetailsPage()),
);
}
but in my CoolDetailsPage it crashes when I do this:
#override
Widget build(BuildContext context) {
final inheritedWidget = CoolApp.of(context);
print(inheritedWidget.randomString); <-- ERROR: The getter 'data' was called on null
return new Text('Cool!');
}
Error Details:
I/flutter ( 6129): ══╡ EXCEPTION CAUGHT BY WIDGETS LIBRARY ╞═══════════════════════════════════════════════════════════
I/flutter ( 6129): The following NoSuchMethodError was thrown building CoolDetailsPage(dirty, state:
I/flutter ( 6129): _CoolDetailsPage#ba0bb):
I/flutter ( 6129): The getter 'data' was called on null.
I/flutter ( 6129): Receiver: null
I/flutter ( 6129): Tried calling: data
I/flutter ( 6129):
I/flutter ( 6129): When the exception was thrown, this was the stack:
I/flutter ( 6129): #0 Object.noSuchMethod (dart:core/runtime/libobject_patch.dart:46:5)
I/flutter ( 6129): #1 CoolApp.of (/lib/main.dart:56:83)
... etc etc
main.dart:56 is return (context.inheritFromWidgetOfExactType(CoolInherit) as CoolInherit).data; and so if my detective work is up to par, I suspect it is something to with navigations/context, which is preventing my final widget from accessing the inheritedWidget, but I'm not sure about that.
UPDATE:
the best I can tell, I need to insert my InheritedWidget at a higher level; before the navigator. so I inserted this into the MaterialApp:
builder: (context, child) {
return new CoolApp(child: child);
},
but that didn't seen to work...
E/flutter (32321): [ERROR:topaz/lib/tonic/logging/dart_error.cc(16)] Unhandled exception:
E/flutter (32321): Navigator operation requested with a context that does not include a Navigator.
E/flutter (32321): The context used to push or pop routes from the Navigator must be that of a widget that is a descendant of a Navigator widget.
E/flutter (32321): #0 Navigator.of.<anonymous closure> (package:flutter/src/widgets/navigator.dart:1180:9)
E/flutter (32321): #1 Navigator.of (package:flutter/src/widgets/navigator.dart:1187:6)
I had the same problem for a long time and I realized that if you wrap the MaterialApp with the Inherited Widget, your data is accessible through the entire app. But in your case, you need to pass data after the user login so to do that you need to create a new Navigator and wrap it with your Inherited Widget. You can see this project https://github.com/johnstef99/inherited-widget-demo
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'InheritedWidget Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyNav(),
);
}
}
Route generatePage(child) {
return MaterialPageRoute(builder: (context) => child);
}
class MyNav extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MyData(
data: 'omg',
child: Navigator(
onGenerateRoute: (settings) {
switch (settings.name) {
case 'page1':
return generatePage(PageOne());
case 'page2':
return generatePage(PageTwo());
case 'page3':
return generatePage(PageThree());
}
},
initialRoute: 'page1',
),
);
}
}
class MyData extends InheritedWidget {
MyData({Key key, this.child, this.data}) : super(key: key, child: child);
final Widget child;
final String data;
static MyData of(BuildContext context) {
return (context.inheritFromWidgetOfExactType(MyData) as MyData);
}
#override
bool updateShouldNotify(MyData oldWidget) {
return true;
}
}
class PageOne extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Page 1'),
),
backgroundColor: Colors.red,
body: RaisedButton(
child: Text("Goto page 2, data=${MyData.of(context).data}"),
onPressed: () {
Navigator.of(context)
.push(MaterialPageRoute(builder: (_) => PageTwo()));
},
),
);
}
}
class PageTwo extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Page 2'),
),
backgroundColor: Colors.red,
body: RaisedButton(
child: Text("Goto page 3, data=${MyData.of(context).data}"),
onPressed: () {
Navigator.of(context)
.push(MaterialPageRoute(builder: (_) => PageThree()));
},
),
);
}
}
class PageThree extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Page 3'),
),
backgroundColor: Colors.green,
body: RaisedButton(
child: Text("Goto page 4, data=${MyData.of(context).data}"),
onPressed: null,
),
);
}
}
That is because you're trying to access CoolApp which is in the route / from another route (dynamic).
But inside your dynamic route, there's no CoolApp. So CoolApp.of(context) returns null, and therefore accessing .data crashes.
You need to find a way to have a CoolApp instance inside your new route.
For more informations, take a look at Get access to the context of InheritedWidget

Resources