Related
My Json response status code is 200 and the object is also created but when I try to bind it with UI in ListView it doesn't show anything. I have used quicktype.io to parse my json data that I received from the API call.
Here's my API_manager.dart file:
import 'package:http/http.dart' as http;
import 'package:aritic/models/contactsModel.dart';
// ignore: camel_case_types
class API_Manager {
Future<ContactsModel> getContacts() async {
var client = http.Client();
var contactsModel;
String contacts_url =
'https://exampleapi.com';
String basicAuth = 'Basic auth key example';
try {
var response = await client.get(contacts_url,
headers: <String, String>{'authorization': basicAuth});
print(response.statusCode);
if (response.statusCode == 200) {
var jsonString = response.body;
var jsonMap = json.decode(jsonString);
contactsModel = contactsModel.fromJson(jsonMap);
}
} catch (Exception) {
return contactsModel;
}
return contactsModel;
}
}
My UI code:
import 'package:aritic/models/contactsModel.dart';
import 'package:aritic/services/api_manager.dart';
class ContactsPage extends StatefulWidget {
#override
_ContactsPageState createState() => _ContactsPageState();
}
class _ContactsPageState extends State<ContactsPage>
with SingleTickerProviderStateMixin {
Future<ContactsModel> _contactsModel;
bool isSearching = false;
TabController _tabController;
#override
void initState() {
// TODO: implement initState
super.initState();
_tabController = TabController(length: 2, initialIndex: 0, vsync: this);
_tabController.addListener(_handleTabIndex);
}
#override
void dispose() {
_tabController.removeListener(_handleTabIndex);
_tabController.dispose();
super.dispose();
}
void _handleTabIndex() {
setState(() {});
}
#override
Widget build(BuildContext context) {
_contactsModel = API_Manager().getContacts();
return DefaultTabController(
length: 2,
child: Scaffold(
appBar: AppBar(
title: Text('Contacts'),
bottom: PreferredSize(
child: Align(
alignment: Alignment.centerLeft,
child: TabBar(
controller: _tabController,
isScrollable: true,
unselectedLabelColor: Colors.white.withOpacity(0.3),
indicatorColor: Colors.white,
tabs: [
Tab(
child: Text('Contacts'),
),
Tab(
child: Text('Companies'),
)
],
),
),
preferredSize: Size.fromHeight(40.0)),
actions: <Widget>[
Padding(
padding: const EdgeInsets.only(right: 16.0),
child: IconButton(
icon: Icon(Icons.search),
color: Colors.white,
onPressed: () {},
),
),
],
),
body: TabBarView(controller: _tabController, children: <Widget>[
Container(
height: double.infinity,
child: FutureBuilder<ContactsModel>(
future: _contactsModel,
builder: (context, snapshot) {
if (snapshot.hasData) {
return ListView.builder(
padding: const EdgeInsets.all(6),
itemCount: snapshot.data.contacts.length,
itemBuilder: (context, index) {
var contact = snapshot.data.contacts[index];
return Container(
height: 100,
color: Colors.white,
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(contact.owner.username,
style: TextStyle(fontSize: 16))
],
),
);
});
} else
return Center(child: CircularProgressIndicator());
})),
Container(
height: double.infinity,
child: ListView(
padding: const EdgeInsets.all(6),
children: <Widget>[
InkWell(
onTap: () {
Navigator.push(context,
MaterialPageRoute(builder: (_) => ViewCompany()));
},
child: Container(
height: 50,
color: Colors.white,
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
'Example company',
style: TextStyle(fontSize: 16),
),
Text(
'Example company',
style: TextStyle(fontSize: 14),
)
],
),
),
),
SizedBox(
height: 5,
),
InkWell(
onTap: () {
Navigator.push(context,
MaterialPageRoute(builder: (_) => ViewCompany()));
},
child: Container(
height: 50,
color: Colors.white,
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
'example',
style: TextStyle(fontSize: 16),
),
Text(
'example',
style: TextStyle(fontSize: 14),
)
],
),
),
),
],
)),
]),
floatingActionButton: _bottomButtons(),
));
}
Widget _bottomButtons() {
return _tabController.index == 0
? FloatingActionButton(
shape: StadiumBorder(),
onPressed: () {
Navigator.push(context, MaterialPageRoute(builder: (_) {
return AddContacts();
}));
},
backgroundColor: Colors.cyan,
child: Icon(
Icons.person_add,
color: Colors.white,
))
: FloatingActionButton(
shape: StadiumBorder(),
onPressed: () {
Navigator.push(context, MaterialPageRoute(builder: (_) {
return AddCompany();
}));
},
backgroundColor: Colors.cyan,
child: Icon(
Icons.add,
color: Colors.white,
),
);
}
}
Created model(contactsModel.dart):
import 'dart:convert';
ContactsModel contactsModelFromJson(String str) => ContactsModel.fromJson(json.decode(str));
String contactsModelToJson(ContactsModel data) => json.encode(data.toJson());
class ContactsModel {
ContactsModel({
this.total,
this.contacts,
});
String total;
Map<String, Contact> contacts;
factory ContactsModel.fromJson(Map<String, dynamic> json) => ContactsModel(
total: json["total"],
contacts: Map.from(json["contacts"]).map((k, v) => MapEntry<String, Contact>(k, Contact.fromJson(v))),
);
Map<String, dynamic> toJson() => {
"total": total,
"contacts": Map.from(contacts).map((k, v) => MapEntry<String, dynamic>(k, v.toJson())),
};
}
class Contact {
Contact({
this.isPublished,
this.dateAdded,
this.dateModified,
this.createdBy,
this.createdByUser,
this.modifiedBy,
this.modifiedByUser,
this.id,
this.points,
this.color,
this.fields,
this.lastActive,
this.owner,
this.ipAddresses,
this.tags,
this.utmtags,
this.stage,
this.dateIdentified,
this.preferredProfileImage,
this.doNotContact,
this.frequencyRules,
});
bool isPublished;
DateTime dateAdded;
DateTime dateModified;
int createdBy;
String createdByUser;
int modifiedBy;
String modifiedByUser;
int id;
int points;
dynamic color;
Fields fields;
dynamic lastActive;
Owner owner;
List<dynamic> ipAddresses;
List<Tag> tags;
List<dynamic> utmtags;
Stage stage;
DateTime dateIdentified;
dynamic preferredProfileImage;
List<dynamic> doNotContact;
List<dynamic> frequencyRules;
factory Contact.fromJson(Map<String, dynamic> json) => Contact(
isPublished: json["isPublished"],
dateAdded: DateTime.parse(json["dateAdded"]),
dateModified: json["dateModified"] == null ? null : DateTime.parse(json["dateModified"]),
createdBy: json["createdBy"],
createdByUser: json["createdByUser"],
modifiedBy: json["modifiedBy"] == null ? null : json["modifiedBy"],
modifiedByUser: json["modifiedByUser"] == null ? null : json["modifiedByUser"],
id: json["id"],
points: json["points"],
color: json["color"],
fields: Fields.fromJson(json["fields"]),
lastActive: json["lastActive"],
owner: Owner.fromJson(json["owner"]),
ipAddresses: List<dynamic>.from(json["ipAddresses"].map((x) => x)),
tags: List<Tag>.from(json["tags"].map((x) => Tag.fromJson(x))),
utmtags: List<dynamic>.from(json["utmtags"].map((x) => x)),
stage: Stage.fromJson(json["stage"]),
dateIdentified: DateTime.parse(json["dateIdentified"]),
preferredProfileImage: json["preferredProfileImage"],
doNotContact: List<dynamic>.from(json["doNotContact"].map((x) => x)),
frequencyRules: List<dynamic>.from(json["frequencyRules"].map((x) => x)),
);
Map<String, dynamic> toJson() => {
"isPublished": isPublished,
"dateAdded": dateAdded.toIso8601String(),
"dateModified": dateModified == null ? null : dateModified.toIso8601String(),
"createdBy": createdBy,
"createdByUser": createdByUser,
"modifiedBy": modifiedBy == null ? null : modifiedBy,
"modifiedByUser": modifiedByUser == null ? null : modifiedByUser,
"id": id,
"points": points,
"color": color,
"fields": fields.toJson(),
"lastActive": lastActive,
"owner": owner.toJson(),
"ipAddresses": List<dynamic>.from(ipAddresses.map((x) => x)),
"tags": List<dynamic>.from(tags.map((x) => x.toJson())),
"utmtags": List<dynamic>.from(utmtags.map((x) => x)),
"stage": stage.toJson(),
"dateIdentified": dateIdentified.toIso8601String(),
"preferredProfileImage": preferredProfileImage,
"doNotContact": List<dynamic>.from(doNotContact.map((x) => x)),
"frequencyRules": List<dynamic>.from(frequencyRules.map((x) => x)),
};
}
class ......{}
Output Screen: here
Json Viewer: here
Dart Devtools: here
This is probably not a problem with your json parsing, but a UI issue. Putting Columns or Rows inside ListViews or the opposite. If there was something wrong with the json, you'd be seeing the return Center(child: CircularProgressIndicator());,
But instead, you're getting an empty UI.
Check your debug console, if you have RenderView errors, or failed to layout render-box issues, this will be the cause of them.
Try using the property of shrink wrap in your lists:
ListView(shrinkwrap:true...
and
ListView.builder(shirinkrap:true...
Also, wrap your Row that inside the ListView with a LimitedBox
i.e
LimitedBox(
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
I have worked a little on a flutter json tutorial, where I am able to fetch and parse json data, I am now trying to put the code into a SliverList. But when I debug my app it throws the follwing error on Builder on line 96: The getter 'length' was called on null. Receiver: null Tried calling: length
I am not sure what to do or what to change. Please note that my app consists of 1 Page with tabbar view.
Here is my full code:
import 'dart:async';
import 'dart:convert';
import 'package:http/http.dart' as http;
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:innbo_app/product_model.dart';
Future<List> fetchData(http.Client client) async {
final response = await client
.get('http://my-json-server.typicode.com/tariksalihovic/innbo/receipt');
// Use the compute function to run parsePhotos in a separate isolate.
return compute(parseData, response.body);
}
// A function that converts a response body into a List<Photo>.
List parseData(String responseBody) {
final parsed = jsonDecode(responseBody).cast<Map<String, dynamic>>();
return parsed.map((json) => Receipt.fromJson(json)).toList();
}
class PlaceholderWidget1 extends StatelessWidget {
final Color color;
PlaceholderWidget1(this.color);
#override
Widget build(BuildContext context) {
return Container(
color: color,
);
}
}
class Indbo1 extends StatelessWidget {
final List data;
Indbo1({Key key, this.data}) : super(key: key);
final List<String> listItems = [];
final List<String> _tabs = <String>[
"Alle",
"Anmeldt",
];
#override
Widget build(BuildContext context) {
return Material(
child: Scaffold(
body: Container(
color: Colors.grey[100],
child: DefaultTabController(
length: _tabs.length, // This is the number of tabs.
child: NestedScrollView(
headerSliverBuilder:
(BuildContext context, bool innerBoxIsScrolled) {
// These are the slivers that show up in the "outer" scroll view.
return <Widget>[
SliverOverlapAbsorber(
handle: NestedScrollView.sliverOverlapAbsorberHandleFor(
context),
sliver: SliverSafeArea(
top: false,
sliver: SliverAppBar(
centerTitle: true,
title: const Text(
"Indbo",
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.w700,
fontSize: 16,
),
),
floating: true,
pinned: true,
snap: true,
stretch: false,
bottom: TabBar(
// These are the widgets to put in each tab in the tab bar.
tabs: _tabs
.map((String name) => Tab(text: name))
.toList(),
),
),
),
),
];
},
body: TabBarView(
// These are the contents of the tab views, below the tabs.
children: _tabs.map((String name) {
return SafeArea(
top: false,
bottom: false,
child: Builder(
builder: (BuildContext context) {
return CustomScrollView(
key: PageStorageKey<String>(name),
slivers: <Widget>[
SliverOverlapInjector(
// This is the flip side of the SliverOverlapAbsorber above.
handle: NestedScrollView
.sliverOverlapAbsorberHandleFor(context),
),
SliverPadding(
padding: EdgeInsets.fromLTRB(15, 0, 15, 10),
sliver: SliverList(
delegate: SliverChildListDelegate([
SizedBox(height: 30),
Text(
'Genstande',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.w700),
),
]),
),
),
SliverList(
delegate: SliverChildListDelegate(
[
Container(
child: FutureBuilder<List>(
future: fetchData(http.Client()),
builder: (context, snapshot) {
if (snapshot.hasError)
print(snapshot.error);
return snapshot.hasData
? Indbo1(data: snapshot.data)
: Center(
child:
CircularProgressIndicator());
},
),
decoration: BoxDecoration(
border: Border(
bottom: BorderSide(
width: 1.0,
color: Colors.grey[200]),
),
color: Colors.white,
),
margin:
EdgeInsets.only(left: 15, right: 15),
),
Container(
decoration: BoxDecoration(
border: Border(
bottom: BorderSide(
width: 1.0,
color: Colors.grey[200]),
),
color: Colors.white,
),
margin:
EdgeInsets.only(left: 15, right: 15),
child: ListView.builder(
itemCount: data.length,
itemBuilder: (context, index) {
return ListTile(
/*onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
Detail(choice: choices[index])),
);
},*/
leading: CircleAvatar(
radius: 26,
backgroundColor: Colors.grey[400],
child: Icon(Icons.phone_iphone,
color: Colors.white),
),
title: Text(
'${data[index].receiptlines[index].name ?? "Empty"}',
style: TextStyle(
color: Colors.black,
fontSize: 16.5,
fontWeight: FontWeight.w700),
),
subtitle: Text(
'${data[index].orderNumber ?? "Empty"}',
style: TextStyle(
color: Colors.grey[700],
fontSize: 12.0),
),
trailing: Text(
'${data[index].price.amount ?? "Empty"}',
style: TextStyle(
color: Colors.black,
fontSize: 16.5,
fontWeight: FontWeight.w800),
),
);
},
),
),
],
),
),
],
);
},
),
);
}).toList(),
),
),
),
),
),
);
}
}
Here is the product_model.dart which is linked to above code:
class Lines {
final String name;
final String productNumber;
Lines({this.name, this.productNumber});
factory Lines.fromJson(Map<String, dynamic> json) {
return Lines(name: json['name'], productNumber: json['productNumber']);
}
}
class Prices {
final int amount;
Prices({this.amount});
factory Prices.fromJson(Map<String, dynamic> json) {
return Prices(
amount: json['amount'],
);
}
}
class Receipt {
final int id;
final String type;
final String receiptId;
final String orderNumber;
final Prices price;
final List<Lines> receiptlines;
Receipt(
{this.id,
this.type,
this.receiptId,
this.orderNumber,
this.price,
this.receiptlines});
factory Receipt.fromJson(Map<String, dynamic> json) {
var list = json['receiptLines'] as List;
List<Lines> receiptLines =
List<Lines>.from(list.map((i) => Lines.fromJson(i))).toList();
return Receipt(
id: json['id'],
type: json['type'],
receiptId: json['receiptId'],
orderNumber: json['orderNumber'],
price: Prices.fromJson(json['price']),
receiptlines: receiptLines
);
}
}
I have the following code
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'dart:async';
import 'dart:convert';
import 'package:http/http.dart' as http;
import 'package:flutter_form_builder/flutter_form_builder.dart';
class MyData {
String title;
String days;
String words;
String rep;
String gender;
var username;
MyData({this.gender, this.title, this.days, this.words, this.rep, this.username,
});
}
class StepperBody extends StatefulWidget {
#override
_StepperBodyState createState() => _StepperBodyState();
}
class _StepperBodyState extends State<StepperBody> {
int currStep = 0;
static var _focusNode = FocusNode();
GlobalKey<FormState> _formKey = GlobalKey<FormState>();
final GlobalKey<FormBuilderState> _fbKey = GlobalKey<FormBuilderState>();
Future<List<String>> _future;
Future<List<String>> _car;
List<GlobalKey<FormState>> formKeys = [
GlobalKey<FormState>(),
GlobalKey<FormState>(),
GlobalKey<FormState>(),
GlobalKey<FormState>()
];
String _key = "786465659081B207EB5BF1EF9AF7552A6";
String _api = "https://10.0.2.2/api/";
Future<void> senddata(List<String> username) async {
final response = await http.post(
_api + "insert_data.php?key=" + _key, body: {
"username": username,
});
var resp = jsonDecode(response.body);
print(resp.toString());
}
Future<List<String>> getData() async {
var url = _api + "get_data.php?key=" + _key;
http.Response response = await http.get(url);
var resp = jsonDecode(response.body);
print(resp.toString());
return resp.map<String>((m) => m['username'] as String).toList();
}
Future<List<String>> getCar() async {
var url = _api + "get_car.php?key=" + _key;
http.Response response = await http.get(url);
var resp = jsonDecode(response.body);
print(resp.toString());
return resp.map<String>((m) => m['plate'] as String).toList();
}
#override
void initState() {
super.initState();
_future = getData();
_car = getCar();
_focusNode.addListener(() {
setState(() {});
print('Has focus: $_focusNode.hasFocus');
});
}
#override
Widget build(BuildContext context) {
void showSnackBarMessage(String message,
[MaterialColor color = Colors.red]) {
Scaffold.of(context).showSnackBar(SnackBar(content: Text(message)));
}
void _submitDetails(List<String> username) {
final FormState formState = _formKey.currentState;
final FormBuilderState fbKeyState = _fbKey.currentState;
/*
_fbKey.currentState.save();
if (_fbKey.currentState.validate()) {
print(_fbKey.currentState.value);
}
*/
if (!fbKeyState.validate()) {
showSnackBarMessage('Please enter correct data');
senddata(username);
} else {
showSnackBarMessage('Saved');
formState.save();
senddata(username);
print("Name: $username");
_fbKey.currentState.save();
if (_fbKey.currentState.validate()) {
print(_fbKey.currentState.value);
}
}
}
return FutureBuilder<List<String>>(
future: Future.wait([_future, _car]),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
final steps = [
Step(
title: const Text('Users'),
//subtitle: const Text('Subtitle'),
isActive: true,
//state: StepState.editing,
state: StepState.indexed,
content: Form(
key: formKeys[0],
child: Column(
children: <Widget>[
FormBuilder(
key: _fbKey,
autovalidate: true,
child: FormBuilderCheckboxList(
decoration:
InputDecoration(labelText: "Languages you know"),
attribute: "languages",
initialValue: ["English"],
options: snapshot.data[0]
.map((languages) => FormBuilderFieldOption(
value: languages, child: Text("$languages")))
.toList(),
),
),
],
),
),
),
Step(
title: const Text('Users'),
//subtitle: const Text('Subtitle'),
isActive: true,
//state: StepState.editing,
state: StepState.indexed,
content: Form(
key: formKeys[1],
child: Column(
children: <Widget>[
FormBuilder(
key: _fbKey,
autovalidate: true,
child: FormBuilderCheckboxList(
decoration:
InputDecoration(labelText: "Cars"),
attribute: "cars",
initialValue: ["BM-WD01"],
options: snapshot.data[1]
.map((car) => FormBuilderFieldOption(
value: car, child: Text("$car")))
.toList(),
),
),
],
),
),
),
];
return Container(
child: Form(
key: _formKey,
child: ListView(children: <Widget>[
Stepper(
steps: steps,
physics: ClampingScrollPhysics(),
type: StepperType.vertical,
currentStep: this.currStep,
onStepContinue: () {
setState(() {
if (formKeys[currStep].currentState.validate()) {
if (currStep < steps.length - 1) {
currStep = currStep + 1;
} else {
currStep = 0;
}
}
// else {
// Scaffold
// .of(context)
// .showSnackBar( SnackBar(content: Text('$currStep')));
// if (currStep == 1) {
// print('First Step');
// print('object' + FocusScope.of(context).toStringDeep());
// }
// }
});
},
controlsBuilder: (BuildContext context,
{VoidCallback onStepContinue, VoidCallback onStepCancel}) {
return Row(
// mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
//
RaisedButton(
color: Colors.red,
child: Text("Forward",
style: TextStyle(color: Colors.white)),
onPressed: onStepContinue,
),
SizedBox(width: 15,),
RaisedButton(
color: Colors.red,
child: Text(
"Back", style: TextStyle(color: Colors.white)),
onPressed: onStepCancel,
),
],
);
},
onStepCancel: () {
setState(() {
if (currStep > 0) {
currStep = currStep - 1;
} else {
currStep = 0;
}
});
},
onStepTapped: (step) {
setState(() {
currStep = step;
});
},
),
RaisedButton(
child: Text(
'Save',
style: TextStyle(color: Colors.white),
),
onPressed: () {
var submitDetails = _submitDetails;
submitDetails(snapshot.data);
},
color: Colors.lightGreen,
),
]),
));
} else {
return CircularProgressIndicator();
}
}
);
}
}
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: "test",),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
MyAppScreenMode createState() => MyAppScreenMode();
}
class MyAppScreenMode extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
return new MaterialApp(
theme: new ThemeData(
primarySwatch: Colors.red,
),
home: new Scaffold(
appBar: new AppBar(
title: new Text('Test stepper'),
),
body: new StepperBody(),
));
}
}
I want to display multiple lists from mySQL, the first function _future works fine, but when I replace future: _future to
future: Future.wait([_future, _car]),
I get an
The element type 'Future<List>' can't be assigned to the list
type 'Future'
I have tried lots of other solutions but none of them works to use snapshot.data[0], snapshot.data1 and so on for more queries and data to display from different tables.
Can't get to work with more futurebuilders which would be even easier for each step own, but then I get
Step(
title: const Text('Users'),
//subtitle: const Text('Subtitle'),
isActive: true,
//state: StepState.editing,
state: StepState.indexed,
content: Form(
key: formKeys[0],
child: FutureBuilder<List>(
future: _future,
// ignore: missing_return
builder: (BuildContext context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
Column(
children: <Widget>[
FormBuilder(
key: _fbKey,
autovalidate: true,
child: FormBuilderCheckboxList(
decoration:
InputDecoration(
labelText: "Languages you know"),
attribute: "languages",
initialValue: ["English"],
options: snapshot.data
.map((gender) =>
FormBuilderFieldOption(
value: gender, child: Text("$gender")))
.toList(),
),
),
],
);
}
}
),
),
),
];
Any help would be appreciated.
The working solution for the question. If there is any better way feel free to post :)
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'dart:async';
import 'dart:convert';
import 'package:http/http.dart' as http;
import 'package:flutter_form_builder/flutter_form_builder.dart';
class MyData {
String title;
String days;
String words;
String rep;
String gender;
var username;
MyData(
{this.gender, this.title, this.days, this.words, this.rep, this.username,
});
}
class StepperBody extends StatefulWidget {
#override
_StepperBodyState createState() => _StepperBodyState();
}
class _StepperBodyState extends State<StepperBody> {
int currStep = 0;
static var _focusNode = FocusNode();
GlobalKey<FormState> _formKey = GlobalKey<FormState>();
final GlobalKey<FormBuilderState> _fbKey = GlobalKey<FormBuilderState>();
Future<List<String>> _future;
Future<List<String>> _getcar;
List<GlobalKey<FormState>> formKeys = [
GlobalKey<FormState>(),
GlobalKey<FormState>(),
GlobalKey<FormState>(),
GlobalKey<FormState>()
];
String _key = "786465659081B207EB5BF1EF9AF7552A6";
String _api = "https://10.0.2.2/api/";
Future<void> senddata() async {
final response = await http.post(
_api + "insert_data.php?key=" + _key, body: {
});
var resp = jsonDecode(response.body);
print(resp.toString());
}
Future<List<String>> getData() async {
var url = _api + "get_data.php?key=" + _key;
http.Response response = await http.get(url);
var resp = jsonDecode(response.body);
print(resp.toString());
return resp.map<String>((m) => m['username'] as String).toList();
}
Future<List<String>> getCar() async {
var url = _api + "get_car.php?key=" + _key;
http.Response response = await http.get(url);
var resp = jsonDecode(response.body);
print(resp.toString());
return resp.map<String>((m) => m['plate'] as String).toList();
}
Widget getdis2(BuildContext context) {
return FutureBuilder<List<String>>(
future: _getcar, // a previously-obtained Future<String> or null
builder: (BuildContext context, AsyncSnapshot<List<String>> snapshot) {
List<Widget> children;
if (snapshot.hasData) {
children = <Widget>[
FormBuilderCheckboxList(
decoration:
InputDecoration(
labelText: "Languages you know"),
attribute: "languages",
initialValue: ["English"],
options: snapshot.data
.map((gender) =>
FormBuilderFieldOption(
value: gender, child: Text("$gender")))
.toList(),
),
];
} else if (snapshot.hasError) {
children = <Widget>[
Icon(
Icons.error_outline,
color: Colors.red,
size: 60,
),
Padding(
padding: const EdgeInsets.only(top: 16),
child: Text('Error: ${snapshot.error}'),
)
];
} else {
children = <Widget>[
SizedBox(
child: CircularProgressIndicator(),
width: 60,
height: 60,
),
const Padding(
padding: EdgeInsets.only(top: 16),
child: Text('Awaiting result...'),
)
];
}
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: children,
),
);
},
);
}
Widget getdis(BuildContext context) {
return FutureBuilder<List<String>>(
future: _future, // a previously-obtained Future<String> or null
builder: (BuildContext context, AsyncSnapshot<List<String>> snapshot) {
List<Widget> children;
if (snapshot.hasData) {
children = <Widget>[
FormBuilderCheckboxList(
decoration:
InputDecoration(
labelText: "Languages you know"),
attribute: "languages",
initialValue: ["English"],
options: snapshot.data
.map((gender) =>
FormBuilderFieldOption(
value: gender, child: Text("$gender")))
.toList(),
),
];
} else if (snapshot.hasError) {
children = <Widget>[
Icon(
Icons.error_outline,
color: Colors.red,
size: 60,
),
Padding(
padding: const EdgeInsets.only(top: 16),
child: Text('Error: ${snapshot.error}'),
)
];
} else {
children = <Widget>[
SizedBox(
child: CircularProgressIndicator(),
width: 60,
height: 60,
),
const Padding(
padding: EdgeInsets.only(top: 16),
child: Text('Awaiting result...'),
)
];
}
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: children,
),
);
},
);
}
#override
void initState() {
super.initState();
_future = getData();
_getcar = getCar();
_focusNode.addListener(() {
setState(() {});
print('Has focus: $_focusNode.hasFocus');
});
}
#override
Widget build(BuildContext context) {
void showSnackBarMessage(String message,
[MaterialColor color = Colors.red]) {
Scaffold.of(context).showSnackBar(SnackBar(content: Text(message)));
}
void _submitDetails() {
final FormState formState = _formKey.currentState;
final FormBuilderState fbKeyState = _fbKey.currentState;
/*
_fbKey.currentState.save();
if (_fbKey.currentState.validate()) {
print(_fbKey.currentState.value);
}
*/
if (!fbKeyState.validate()) {
showSnackBarMessage('Please enter correct data');
senddata();
} else {
showSnackBarMessage('Saved');
formState.save();
senddata();
print("Name: ");
_fbKey.currentState.save();
if (_fbKey.currentState.validate()) {
print(_fbKey.currentState.value);
}
}
}
final steps = [
Step(
title: const Text('Users'),
//subtitle: const Text('Subtitle'),
isActive: true,
//state: StepState.editing,
state: StepState.indexed,
content: Form(
key: formKeys[0],
child: getdis(context),
),
),
Step(
title: const Text('Cars'),
//subtitle: const Text('Subtitle'),
isActive: true,
//state: StepState.editing,
state: StepState.indexed,
content: Form(
key: formKeys[1],
child: getdis2(context),
),
),
];
return Container(
child: Form(
key: _formKey,
child: ListView(children: <Widget>[
Stepper(
steps: steps,
physics: ClampingScrollPhysics(),
type: StepperType.vertical,
currentStep: this.currStep,
onStepContinue: () {
setState(() {
if (formKeys[currStep].currentState.validate()) {
if (currStep < steps.length - 1) {
currStep = currStep + 1;
} else {
currStep = 0;
}
}
// else {
// Scaffold
// .of(context)
// .showSnackBar( SnackBar(content: Text('$currStep')));
// if (currStep == 1) {
// print('First Step');
// print('object' + FocusScope.of(context).toStringDeep());
// }
// }
});
},
controlsBuilder: (BuildContext context,
{VoidCallback onStepContinue, VoidCallback onStepCancel}) {
return Row(
// mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
//
RaisedButton(
color: Colors.red,
child: Text("Forward",
style: TextStyle(color: Colors.white)),
onPressed: onStepContinue,
),
SizedBox(width: 15,),
RaisedButton(
color: Colors.red,
child: Text(
"Back", style: TextStyle(color: Colors.white)),
onPressed: onStepCancel,
),
],
);
},
onStepCancel: () {
setState(() {
if (currStep > 0) {
currStep = currStep - 1;
} else {
currStep = 0;
}
});
},
onStepTapped: (step) {
setState(() {
currStep = step;
});
},
),
RaisedButton(
child: Text(
'Save',
style: TextStyle(color: Colors.white),
),
onPressed: () {
var submitDetails = _submitDetails;
submitDetails();
},
color: Colors.lightGreen,
),
]),
));
}
}
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: "test",),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
MyAppScreenMode createState() => MyAppScreenMode();
}
class MyAppScreenMode extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
return new MaterialApp(
theme: new ThemeData(
primarySwatch: Colors.red,
),
home: new Scaffold(
appBar: new AppBar(
title: new Text('Test stepper'),
),
body: new StepperBody(),
));
}
}
Im not sure about this, but in my understanding, a FutureBuilder expects exactly 1 future. the returned value of this future is accessable with "snapshot.data".
If you need to wait for multiple futures to resolve, I propose you use multiple FutureBuilders.
Another way would be to use Future.wait() somewhere else and return all required data as one Furure from there.
I have created Gridview builder through JSON data but when i navigate to new Page, I am getting Error: Could not find the correct Provider above this SingleItemScreen Widget
To fix, please:
Ensure the Provider is an ancestor to this SingleItemScreen Widget
Provide types to Provider
Provide types to Consumer
Provide types to Provider.of()
Ensure the correct context is being used.
I am not sure where I am going wrong with this.
List<Product> productFromJson(String str) => List<Product>.from(json.decode(str).map((x) => Product.fromJson(x)));
String productToJson(List<Product> data) => json.encode(List<dynamic>.from(data.map((x) => x.toJson())));
class Product {
String productId;
String sku;
String itemName;
String listPrice;
String publishedPrice;
String onsale;
String stockQuantity;
StockStatus stockStatus;
String ratingCount;
String averageRating;
String totalSales;
String imagePath;
String category;
Product({
this.productId,
this.sku,
this.itemName,
this.listPrice,
this.publishedPrice,
this.onsale,
this.stockQuantity,
this.stockStatus,
this.ratingCount,
this.averageRating,
this.totalSales,
this.imagePath,
this.category,
});
factory Product.fromJson(Map<String, dynamic> json) => Product(
productId: json["product_id"],
sku: json["sku"],
itemName: json["item_name"],
listPrice: json["list_price"],
publishedPrice: json["published_price"],
onsale: json["onsale"],
stockQuantity: json["stock_quantity"],
stockStatus: stockStatusValues.map[json["stock_status"]],
ratingCount: json["rating_count"],
averageRating: json["average_rating"],
totalSales: json["total_sales"],
imagePath: json["image_path"],
category: json["category"],
);
Map<String, dynamic> toJson() => {
"product_id": productId,
"sku": sku,
"item_name": itemName,
"list_price": listPrice,
"published_price": publishedPrice,
"onsale": onsale,
"stock_quantity": stockQuantity,
"stock_status": stockStatusValues.reverse[stockStatus],
"rating_count": ratingCount,
"average_rating": averageRating,
"total_sales": totalSales,
"image_path": imagePath,
"category": category,
};
}
enum StockStatus { INSTOCK }
final stockStatusValues = EnumValues({
"instock": StockStatus.INSTOCK
});
class EnumValues<T> {
Map<String, T> map;
Map<T, String> reverseMap;
EnumValues(this.map);
Map<T, String> get reverse {
if (reverseMap == null) {
reverseMap = map.map((k, v) => new MapEntry(v, k));
}
return reverseMap;
}
}
Future<List<Product>> fetchPhotos(http.Client client) async {
final response =
await client.get('http://flutter.bizsupplier.in/product.php');
return compute(parsePhotos, response.body);
}
Future<List<Product>> parsePhotos(String responseBody) async {
final parsed = jsonDecode(responseBody).cast<Map<String, dynamic>>();
return parsed.map<Product>((json) => Product.fromJson(json)).toList();
}
class MyHomePage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return FutureBuilder<List<Product>>(
future: fetchPhotos(http.Client()),
builder: (context, snapshot) {
if (snapshot.hasError) print(snapshot.error);
if (snapshot.hasData) {
return PhotosList(product: snapshot.data);
} else {
return Center(child: CircularProgressIndicator());
}
},
);
}
}
class PhotosList extends StatelessWidget {
final List<Product> product;
PhotosList({Key key, this.product}) : super(key: key);
#override
Widget build(BuildContext context) {
return GridView.builder(
shrinkWrap: true,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
),
itemCount: product.length,
itemBuilder: (context, index) {
return Card(
child: Column(
children: <Widget>[
Container(
height: 150,
child: GestureDetector(
onTap: (){
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => SingleItemScreen(),
settings: RouteSettings(
arguments: product[index]
)
)
);
},
child: Image.network(product[index].imagePath)),
),
Expanded(
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
RaisedButton(
onPressed: () {},
child: Text('Buy Now'),
color: Colors.redAccent,
),
],
),
)
],
),
);
},
);
}
}
class SingleItemScreen extends StatelessWidget {
final List<Product> product;
SingleItemScreen({Key key, this.product}) : super(key: key);
#override
Widget build(BuildContext context) {
final Product product = Provider.of<Product>(context, listen: false);
return Scaffold(
appBar: AppBar(
title: Text('Test PhP Navigation'),
actions: <Widget>[
new IconButton(
icon: Icon(
Icons.search,
color: Colors.white,
),
onPressed: () {}),
new IconButton(
icon: Icon(
Icons.shopping_cart,
color: Colors.white,
),
onPressed: () {}),
],
),
body: SingleChildScrollView(
child: Column(
children: <Widget>[
Container(
height: 300,
child: Image.network(product.imagePath),
),
Container(
child: Text(product.productId),
),
],
),
),
bottomNavigationBar: Container(
width: MediaQuery.of(context).size.width,
height: 50.0,
child: Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
Flexible(
fit: FlexFit.tight,
flex: 1,
child: RaisedButton(
onPressed: () {},
color: Colors.grey,
child: Center(
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Icon(
Icons.list,
color: Colors.white,
),
SizedBox(
width: 4.0,
),
Text(
"SAVE",
style: TextStyle(color: Colors.white),
),
],
),
),
),
),
Flexible(
flex: 2,
child: RaisedButton(
onPressed: (){},
color: Colors.greenAccent,
child: Center(
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Icon(
Icons.card_travel,
color: Colors.white,
),
SizedBox(
width: 4.0,
),
Text(
"ADD TO BAG",
style: TextStyle(color: Colors.white),
),
],
),
),
),
),
],
),
));
}
}
You can copy paste run full code below
Step 1: remark settings and use SingleItemScreen(product: product[index])
MaterialPageRoute(
builder: (context) =>
SingleItemScreen(product: product[index]),
/*settings: RouteSettings(
arguments: product[index]
)*/
));
Step 2: Modify SingleItemScreen to accept product
class SingleItemScreen extends StatelessWidget {
final Product product;
SingleItemScreen({Key key, this.product}) : super(key: key);
Step 3: Remark Provider
//final Product product = Provider.of<Product>(context, listen: false);
working demo
full code
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'dart:convert';
import 'package:http/http.dart' as http;
List<Product> productFromJson(String str) =>
List<Product>.from(json.decode(str).map((x) => Product.fromJson(x)));
String productToJson(List<Product> data) =>
json.encode(List<dynamic>.from(data.map((x) => x.toJson())));
class Product {
String productId;
String sku;
String itemName;
String listPrice;
String publishedPrice;
String onsale;
String stockQuantity;
StockStatus stockStatus;
String ratingCount;
String averageRating;
String totalSales;
String imagePath;
String category;
Product({
this.productId,
this.sku,
this.itemName,
this.listPrice,
this.publishedPrice,
this.onsale,
this.stockQuantity,
this.stockStatus,
this.ratingCount,
this.averageRating,
this.totalSales,
this.imagePath,
this.category,
});
factory Product.fromJson(Map<String, dynamic> json) => Product(
productId: json["product_id"],
sku: json["sku"],
itemName: json["item_name"],
listPrice: json["list_price"],
publishedPrice: json["published_price"],
onsale: json["onsale"],
stockQuantity: json["stock_quantity"],
stockStatus: stockStatusValues.map[json["stock_status"]],
ratingCount: json["rating_count"],
averageRating: json["average_rating"],
totalSales: json["total_sales"],
imagePath: json["image_path"],
category: json["category"],
);
Map<String, dynamic> toJson() => {
"product_id": productId,
"sku": sku,
"item_name": itemName,
"list_price": listPrice,
"published_price": publishedPrice,
"onsale": onsale,
"stock_quantity": stockQuantity,
"stock_status": stockStatusValues.reverse[stockStatus],
"rating_count": ratingCount,
"average_rating": averageRating,
"total_sales": totalSales,
"image_path": imagePath,
"category": category,
};
}
enum StockStatus { INSTOCK }
final stockStatusValues = EnumValues({"instock": StockStatus.INSTOCK});
class EnumValues<T> {
Map<String, T> map;
Map<T, String> reverseMap;
EnumValues(this.map);
Map<T, String> get reverse {
if (reverseMap == null) {
reverseMap = map.map((k, v) => new MapEntry(v, k));
}
return reverseMap;
}
}
Future<List<Product>> fetchPhotos(http.Client client) async {
final response =
await client.get('http://flutter.bizsupplier.in/product.php');
return compute(parsePhotos, response.body);
}
Future<List<Product>> parsePhotos(String responseBody) async {
final parsed = jsonDecode(responseBody).cast<Map<String, dynamic>>();
return parsed.map<Product>((json) => Product.fromJson(json)).toList();
}
class MyHomePage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return FutureBuilder<List<Product>>(
future: fetchPhotos(http.Client()),
builder: (context, snapshot) {
if (snapshot.hasError) print(snapshot.error);
if (snapshot.hasData) {
return PhotosList(product: snapshot.data);
} else {
return Center(child: CircularProgressIndicator());
}
},
);
}
}
class PhotosList extends StatelessWidget {
final List<Product> product;
PhotosList({Key key, this.product}) : super(key: key);
#override
Widget build(BuildContext context) {
return GridView.builder(
shrinkWrap: true,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
),
itemCount: product.length,
itemBuilder: (context, index) {
return Card(
child: Column(
children: <Widget>[
Container(
height: 150,
child: GestureDetector(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
SingleItemScreen(product: product[index]),
/*settings: RouteSettings(
arguments: product[index]
)*/
));
},
child: Image.network(product[index].imagePath)),
),
Expanded(
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
RaisedButton(
onPressed: () {},
child: Text('Buy Now'),
color: Colors.redAccent,
),
],
),
)
],
),
);
},
);
}
}
class SingleItemScreen extends StatelessWidget {
final Product product;
SingleItemScreen({Key key, this.product}) : super(key: key);
#override
Widget build(BuildContext context) {
//final Product product = Provider.of<Product>(context, listen: false);
return Scaffold(
appBar: AppBar(
title: Text('Test PhP Navigation'),
actions: <Widget>[
new IconButton(
icon: Icon(
Icons.search,
color: Colors.white,
),
onPressed: () {}),
new IconButton(
icon: Icon(
Icons.shopping_cart,
color: Colors.white,
),
onPressed: () {}),
],
),
body: SingleChildScrollView(
child: Column(
children: <Widget>[
Container(
height: 300,
child: Image.network(product.imagePath),
),
Container(
child: Text(product.productId),
),
],
),
),
bottomNavigationBar: Container(
width: MediaQuery.of(context).size.width,
height: 50.0,
child: Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
Flexible(
fit: FlexFit.tight,
flex: 1,
child: RaisedButton(
onPressed: () {},
color: Colors.grey,
child: Center(
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Icon(
Icons.list,
color: Colors.white,
),
SizedBox(
width: 4.0,
),
Text(
"SAVE",
style: TextStyle(color: Colors.white),
),
],
),
),
),
),
Flexible(
flex: 2,
child: RaisedButton(
onPressed: () {},
color: Colors.greenAccent,
child: Center(
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Icon(
Icons.card_travel,
color: Colors.white,
),
SizedBox(
width: 4.0,
),
Text(
"ADD TO BAG",
style: TextStyle(color: Colors.white),
),
],
),
),
),
),
],
),
));
}
}
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: MyHomePage(),
);
}
}
I'd like to create a shopping cart app
I had a problem
How to create a GridView within a ListView by flutter with JSON API
I want it exactly like this Example :
https://i.stack.imgur.com/2KQFG.png
https://i.stack.imgur.com/I0gY8.gif
--- Update ----
About SliverGrid
I tried to fetch the products but an error appeared (This is regarding the SliverGrid part)
I/flutter ( 5992): ══╡ EXCEPTION CAUGHT BY WIDGETS LIBRARY ╞═══════════════════════════════════════════════════════════
I/flutter ( 5992): The following assertion was thrown building FutureBuilder<List<dynamic>>(dirty, state:
I/flutter ( 5992): _FutureBuilderState<List<dynamic>>#78747):
I/flutter ( 5992): A build function returned null.
I/flutter ( 5992): The offending widget is: FutureBuilder<List<dynamic>>
I/flutter ( 5992): Build functions must never return null. To return an empty space that causes the building widget to
I/flutter ( 5992): fill available room, return "new Container()". To return an empty space that takes as little room as
I/flutter ( 5992): possible, return "new Container(width: 0.0, height: 0.0)".
With regard to ListView (It works fine)..
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';
import 'dart:async';
Future<List<dynamic>> getCategoriesApi() async {
http.Response response1 =
await http.get("http://159.89.228.206/");
Map<String, dynamic> decodedCategories = json.decode(response1.body);
//print(response1);
return decodedCategories['categories'];
}
Future<List<dynamic>> getProductsApi() async {
http.Response response =
await http.get("http://159.89.228.206/");
Map<String, dynamic> decodedCategories2 = json.decode(response.body);
// print(response);
return decodedCategories2['last'];
}
void main() => runApp(new MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new MaterialApp(
home: new MyHomePage(title: 'Sliver Demo'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => new _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
final ScrollController _scrollController = new ScrollController();
List<dynamic> products;
List<dynamic> categories;
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text(widget.title),
),
body: Column(children: <Widget>[
Expanded(
child: CustomScrollView(
controller: _scrollController,
slivers: <Widget>[
SliverToBoxAdapter(
child: SizedBox(
height: 120.0,
child: FutureBuilder(
future: getCategoriesApi(),
builder: (BuildContext context,
AsyncSnapshot<List<dynamic>> snapshot) {
if (snapshot.connectionState ==
ConnectionState.done) {
return ListView.builder(
scrollDirection: Axis.horizontal,
itemBuilder: (context, index) {
Map<String, String> category =
snapshot.data[index].cast<String, String>();
return Card(
child: Container(
height: double.infinity,
color: Colors.grey[200],
child: Center(
child: Padding(
padding: EdgeInsets.all(30.0),
child: Text(category["name"]),
),
),
),
);
},
itemCount: snapshot.data.length,
);
} else {
return Center(child: CircularProgressIndicator());
}
}),
),
),
SliverToBoxAdapter(
child: Container(
child: FutureBuilder(
future: getProductsApi(),
builder: (BuildContext context,
AsyncSnapshot<List<dynamic>> snapshot) {
if (snapshot.connectionState ==
ConnectionState.done) {
SliverGrid(
gridDelegate:
SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
childAspectRatio: 0.8,
),
delegate: SliverChildBuilderDelegate(
(context, index) {
Map<String, String> product = snapshot
.data[index]
.cast<String, String>();
return Card(
child: Container(
height: double.infinity,
color: Colors.grey[200],
child: Center(
child: Padding(
padding: EdgeInsets.all(30.0),
child: Text(product["name"]),
),
),
),
);
},
childCount: snapshot.data.length,
),
);
} else {
return Center(child: CircularProgressIndicator());
}
}),
),
),
],
),
)
]));
}
}
You can't embed a GridView directly in a ListView unless you play with the height reserved for the GridView. If you want to maintain the scroll for both sections as you show in your images, the best thing is to use a CustomScrollView and Slivers.
After the image is my proposal.
import 'dart:convert';
import 'package:flutter/material.dart';
String productsJson =
'{"last": [{"product_id":"62","thumb":"sandwich.png","name":"Test Tilte","price":"\$55.00"}, '
'{"product_id":"61","thumb":"sandwich.png","name":"Test Tilte","price":"\$55.00"}, '
'{"product_id":"57","thumb":"sandwich.png","name":"Test Tilte","price":"\$55.00"}, '
'{"product_id":"63","thumb":"sandwich.png","name":"Test Tilte","price":"\$55.00"}, '
'{"product_id":"64","thumb":"sandwich.png","name":"Test Tilte","price":"\$55.00"}, '
'{"product_id":"58","thumb":"sandwich.png","name":"Test Tilte","price":"\$55.00"}, '
'{"product_id":"59","thumb":"sandwich.png","name":"Test Tilte","price":"\$55.00"}]}';
String categoriesJson = '{"categories":['
'{"name":"Category 1","image":"icon.png","id":2}, '
'{"name":"Category 2","image":"icon.png","id":4}, '
'{"name":"Category 3","image":"icon.png","id":4}, '
'{"name":"Category 4","image":"icon.png","id":4}, '
'{"name":"Category 5","image":"icon.png","id":6}]}';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: MyHomePage(title: 'Sliver Demo'),
);
}
}
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 ScrollController _scrollController = ScrollController();
List<dynamic> products;
List<dynamic> categories;
#override
initState() {
super.initState();
Map<String, dynamic> decoded = json.decode(productsJson);
products = decoded['last'];
Map<String, dynamic> decodedCategories = json.decode(categoriesJson);
categories = decodedCategories['categories'];
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: CustomScrollView(
controller: _scrollController,
slivers: <Widget>[
SliverToBoxAdapter(
child: SizedBox(
height: 120.0,
child: ListView.builder(
scrollDirection: Axis.horizontal,
itemBuilder: (context, index) {
Map<String, String> category =
categories[index].cast<String, String>();
return Card(
child: Container(
height: double.infinity,
color: Colors.grey[200],
child: Center(
child: Padding(
padding: EdgeInsets.all(30.0),
child: Text(category["name"]),
),
),
),
);
},
itemCount: categories.length,
),
),
),
SliverGrid(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
childAspectRatio: 0.8,
),
delegate: SliverChildBuilderDelegate(
(BuildContext context, int index) {
Map<String, String> product =
products[index].cast<String, String>();
return Card(
child: Container(
color: Colors.grey[400],
child: Padding(
padding: EdgeInsets.symmetric(vertical: 30.0),
child: Center(
child: Text("Product ${product["product_id"]}")),
),
),
);
},
childCount: products.length,
),
),
],
),
);
}
}
If you want to retrieve the json from the network you can add/replace the following code. Add a method that returns a Future and then build the ListView using a FutureBuilder.
....
import 'package:http/http.dart' as http;
import 'dart:async';
....
Future<List<dynamic>> getCategories() async {
http.Response response = await http.get("http://159.89.228.206");
Map<String, dynamic> decodedCategories = json.decode(response.body);
return decodedCategories['categories'];
}
...
...
SliverToBoxAdapter(
child: SizedBox(
height: 120.0,
child: FutureBuilder(
future: getCategories(),
builder: (BuildContext context, AsyncSnapshot<List<dynamic>> snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
return ListView.builder(
scrollDirection: Axis.horizontal,
itemBuilder: (context, index) {
Map<String, String> category =
snapshot.data[index].cast<String, String>();
return Card(
child: Container(
height: double.infinity,
color: Colors.grey[200],
child: Center(
child: Padding(
padding: EdgeInsets.all(30.0),
child: Text(category["name"]),
),
),
),
);
},
itemCount: snapshot.data.length,
);
} else {
return Center(child: CircularProgressIndicator());
}
}),
),
),
....
The simple answer to this would be Tabs .
Render Tabs dynamically as per your category and under render GridView in TabView.
Here is Output :
Here is the Code :
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Shopping App',
theme: ThemeData(
primarySwatch: Colors.orange,
),
home: ShowProductScreen(),
);
}
}
class Product {
String productId;
String image;
String name;
String price;
Product({this.productId, this.image, this.name, this.price});
}
class Category {
String id;
String name;
String image;
List<Product> productList;
Category({this.id, this.name, this.image, this.productList});
}
class ShowProductScreen extends StatefulWidget {
#override
_ShowProductScreenState createState() => _ShowProductScreenState();
}
class _ShowProductScreenState extends State<ShowProductScreen> with TickerProviderStateMixin {
List<Category> categoryList = List();
TabController _tabController;
#override
void initState() {
super.initState();
//Add data
for (int i = 0; i < 10; i++) {
List<Product> productList = List();
for (int j = 0; j < 50; j++) {
Product product = Product(
productId: "$i-$j",
price: "${(j + 1) * 10}",
name: "Product $i-$j",
image: "assets/image.jpg",
);
productList.add(product);
}
Category category = Category(
id: "$i",
name: "Category $i",
image: "assets/image.jpg",
productList: productList,
);
categoryList.add(category);
}
_tabController = TabController(vsync: this, length: categoryList.length);
}
#override
void dispose() {
super.dispose();
_tabController.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
titleSpacing: 0.0,
title: IconButton(
icon: Icon(
Icons.shopping_cart,
),
onPressed: () {},
),
actions: <Widget>[
IconButton(
icon: Icon(
Icons.menu,
),
onPressed: () {},
)
],
),
body: Column(
children: <Widget>[
Container(
height: 100.0,
child: TabBar(
controller: _tabController,
isScrollable: true,
tabs: categoryList.map((Category category) {
return CategoryWidget(
category: category,
);
}).toList(),
),
),
Expanded(
child: Container(
padding: EdgeInsets.all(5.0),
child: TabBarView(
controller: _tabController,
children: categoryList.map((Category category) {
return Container(
child: GridView.count(
crossAxisCount: 2,
childAspectRatio: 4 / 3,
controller: ScrollController(keepScrollOffset: false),
scrollDirection: Axis.vertical,
children: category.productList.map(
(Product product) {
return ProductWidget(
product: product,
);
},
).toList(),
),
);
}).toList(),
),
),
)
],
),
);
}
}
class CategoryWidget extends StatelessWidget {
final Category category;
const CategoryWidget({Key key, this.category}) : super(key: key);
#override
Widget build(BuildContext context) {
return Container(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
Padding(
padding: const EdgeInsets.all(4.0),
child: Image(
image: AssetImage(category.image),
height: 60.0,
),
),
Text(category.name)
],
),
);
}
}
class ProductWidget extends StatelessWidget {
final Product product;
const ProductWidget({Key key, this.product}) : super(key: key);
#override
Widget build(BuildContext context) {
return Container(
margin: EdgeInsets.all(4.0),
decoration: BoxDecoration(
borderRadius: BorderRadius.all(
Radius.circular(8.0),
),
border: Border.all(
color: Colors.orange,
),
),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
Image(
image: AssetImage(product.image),
fit: BoxFit.contain,
height: 80.0,
),
Text(product.name)
],
),
);
}
}
Hope it Helps!