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(),
);
}
}
Related
I made a simple store app where I put data in a json file. I've managed to call the data into the Tabbar & TabbarView by FutureBuilder. Also I put the list of these products in a drawer. I want if I click on one of the product names from Drawer, it will navigate to specific TabBar respective IDs.
I've done this trick before, but only on static pages, not FutureBuilder like this. I really appreciate any help
HomePage.dart
import 'dart:convert';
import 'package:contoh_tabbar_json/model.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
class HomePage extends StatefulWidget {
const HomePage({Key key}) : super(key: key);
#override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> with SingleTickerProviderStateMixin {
Future<List<Product>> _getProduct() async {
final dataString = await rootBundle.loadString('assets/myproduct.json');
final List<dynamic> json = jsonDecode(dataString);
final products = <Product>[];
for (var v in json) {
products.add(Product.fromJson(v));
}
return products;
}
#override
void initState() {
_getProduct();
super.initState();
}
#override
void dispose() {
super.dispose();
}
Product product;
#override
Widget build(BuildContext context) {
return FutureBuilder<List<Product>>(
future: _getProduct(),
builder: (context, mydata) {
if (mydata.hasData) {
List<Tab> tabs = <Tab>[];
for (int index = 0; index < mydata.data.length; index++) {
tabs.add(Tab(
child: Text(
mydata.data[index].title,
),
));
}
return DefaultTabController(
length: mydata.data.length,
child: Scaffold(
endDrawer: Drawer(
child: SingleChildScrollView(
physics: const ScrollPhysics(),
child: Column(
children: [
Center(
child: Container(
height: 100,
alignment: Alignment.center,
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 5),
decoration: const BoxDecoration(
borderRadius: BorderRadius.all(
Radius.circular(50),
),
),
child: const Text("Product List",
style: TextStyle(
fontSize: 17,
fontWeight: FontWeight.w700,
),
textAlign: TextAlign.center),
),
),
),
//
ListView.builder(
physics: const NeverScrollableScrollPhysics(),
shrinkWrap: true,
itemCount: mydata.data.length,
itemBuilder: (BuildContext context, index) {
return ListTile(
dense: true,
title: Text(mydata.data[index].title, style: const TextStyle(fontSize: 15, fontWeight: FontWeight.w500)),
onTap: () {
Navigator.pop(context);
},
);
}),
const SizedBox(height: 70),
],
),
),
),
appBar: AppBar(
title: const Text("My Store"),
bottom: TabBar(
isScrollable: true,
tabs: tabs,
),
),
body: FutureBuilder<List<Product>>(
future: _getProduct(),
builder: (context, mydata) {
if (mydata.hasData) {
List<Widget> tabs = <Widget>[];
for (int index = 0; index < mydata.data.length; index++) {
tabs.add(Tab(
child: SingleChildScrollView(
physics: const BouncingScrollPhysics(parent: AlwaysScrollableScrollPhysics()),
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 0),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: [
const SizedBox(height: 30),
Text(
mydata.data[index].title,
textAlign: TextAlign.center,
style: const TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
),
const SizedBox(height: 30),
Text(mydata.data[index].title),
const SizedBox(height: 30),
Text(mydata.data[index].description),
const SizedBox(height: 30),
Text(mydata.data[index].category),
],
),
),
),
));
}
return TabBarView(
children: tabs,
);
}
return Container();
},
),
),
);
}
return Scaffold(
body: Center(child: Text(mydata.hasError ? mydata.error.toString() : "Loading...")),
);
},
);
}
}
Model
import 'dart:convert';
List<Product> productFromJson(String str) =>
List<Product>.from(json.decode(str).map((x) => Product.fromJson(x)));
class Product {
Product({
this.id,
this.title,
this.description,
this.category,
});
final int id;
final String title;
final String description;
final String category;
factory Product.fromJson(Map<String, dynamic> json) => Product(
id: json["id"],
title: json["title"],
description: json["description"],
category: json["category"],
);
String getTitle() {
return title;
}
String getDescription() {
return description;
}
String getCategory() {
return category;
}
}
Mungkin bisa dicek pada github saya :
https://github.com/kuldii/navigate_drawer_tabbar
How do i generate JSON from multiple lists using GetX package? I am using GetX package for Flutter to pass data between screens in Flutter. The list is obtained from Episode5.dart file. The list is vieewed in MyApp file which is also the place where JSON Generate Button is kept. On clicking the button, i want the json created using the data in the list above. I have the following code:
1. Episode5.dart
class Episode5 extends StatefulWidget {
#override
_Episode5State createState() => _Episode5State();
}
class _Episode5State extends State<Episode5> {
TextEditingController nameController = TextEditingController();
TextEditingController emailController = TextEditingController();
final form = GlobalKey<FormState>();
static var _focusNode = new FocusNode();
bool update = false;
int currentIndex = 0;
List<User> userList = [
User(name: "a", email: "a"),
User(name: "d", email: "b"),
User(name: "c", email: "c"),
];
#override
Widget build(BuildContext context) {
Widget bodyData() => DataTable(
onSelectAll: (b) {},
sortColumnIndex: 0,
sortAscending: true,
columns: <DataColumn>[
DataColumn(label: Text("Name"), tooltip: "To Display name"),
DataColumn(label: Text("Email"), tooltip: "To Display Email"),
DataColumn(label: Text("Update"), tooltip: "Update data"),
],
rows: userList
.map(
(user) => DataRow(
cells: [
DataCell(
Text(user.name),
),
DataCell(
Text(user.email),
),
DataCell(
IconButton(
onPressed: () {
currentIndex = userList.indexOf(user);
_updateTextControllers(user); // new function here
},
icon: Icon(
Icons.edit,
color: Colors.black,
),
),
),
],
),
)
.toList(),
);
return Scaffold(
appBar: AppBar(
title: Text("Data add to List Table using Form"),
),
body: Container(
child: Column(
children: <Widget>[
bodyData(),
Padding(
padding: EdgeInsets.all(10.0),
child: Form(
key: form,
child: Container(
child: Column(
children: <Widget>[
TextFormField(
controller: nameController,
focusNode: _focusNode,
keyboardType: TextInputType.text,
autocorrect: false,
maxLines: 1,
validator: (value) {
if (value.isEmpty) {
return 'This field is required';
}
return null;
},
decoration: new InputDecoration(
labelText: 'Name',
hintText: 'Name',
labelStyle: new TextStyle(
decorationStyle: TextDecorationStyle.solid),
),
),
SizedBox(
height: 10,
),
TextFormField(
controller: emailController,
keyboardType: TextInputType.text,
autocorrect: false,
maxLines: 1,
validator: (value) {
if (value.isEmpty) {
return 'This field is required';
}
return null;
},
decoration: new InputDecoration(
labelText: 'Email',
hintText: 'Email',
labelStyle: new TextStyle(
decorationStyle: TextDecorationStyle.solid)),
),
SizedBox(
height: 10,
),
Column(
children: <Widget>[
Center(
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
TextButton(
child: Text("Add"),
onPressed: () {
form.currentState.save();
addUserToList(
nameController.text,
emailController.text,
);
},
),
TextButton(
child: Text("Update"),
onPressed: () {
form.currentState.save();
updateForm();
},
),
],
),
Row(
crossAxisAlignment: CrossAxisAlignment.end,
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
ElevatedButton(
child: Text("Save and Exit"),
onPressed: () {
form.currentState.save();
addUserToList(
nameController.text,
emailController.text,
);
Navigator.pop(context, userList);
},
),
],
),
],
),
),
],
),
],
),
),
),
),
],
),
),
);
}
void updateForm() {
setState(() {
User user = User(name: nameController.text, email: emailController.text);
userList[currentIndex] = user;
});
}
void _updateTextControllers(User user) {
setState(() {
nameController.text = user.name;
emailController.text = user.email;
});
}
void addUserToList(name, email) {
setState(() {
userList.add(User(name: name, email: email));
});
}
}
1. Main.dart
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
return Scaffold(
appBar: AppBar(
title: Text("Testing List View Data From second page to first page"),
),
body: Column(
children: <Widget>[
Expanded(
child: GetBuilder<FormController>(
builder: (controller) => ListView.builder(
itemCount: controller.userList.length,
itemBuilder: (context, index) => Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
Text(controller.userList[index].name),
Text(controller.userList[index].email),
],
),
),
),
),
SizedBox(
height: 10.0,
),
ElevatedButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => Episode5(),
),
);
},
child: Text("Go to Form"),
),
SizedBox(
height: 10.0,
),
ElevatedButton(
onPressed: () {
generateJSON();
},
child: Text("Generate JSON"),
),
],
),
);
}
generateJSON() {
GenerateJSON generate =
GenerateJSON(controller.userList, controller.schooList);
String jsonAddress = jsonEncode(generate);
print(jsonAddress);
}
}
2. Model.dart
class User {
String name;
String email;
User({this.name, this.email});
Map toJson() => {
'name': name,
'age': email,
};
}
class GenerateJSON {
List<User> user;
List<School> school;
GenerateJSON([this.user, this.school]);
Map toJson() {
List<Map> user =
this.user != null ? this.user.map((e) => e.toJson()).toList() : null;
return {'User': user, 'School': school};
}
}
It's exist now a powerful and flexible package on pub.dev named json_bridge.
For those who want to deal with json file, checkout it here: https://pub.dev/packages/json_bridge
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 a problem with the switch widget in flutter. I only want to execute the onChanged function when I activate it but every time I click on the switch even when it's not active, it executes the function and I have the popup menu that appears.
new Switch(
value: false,
onChanged: (bool isOn) {
if(isOn){
setState(() {
return showDialog(
context: context,
barrierDismissible: false,
builder: (context) {
return AlertDialog(
key: _alertDialogKey,
contentPadding: EdgeInsets.only(left: 25, right: 25),
title: Center(
child: Text("Choisissez une filiale")
),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(
Radius.circular(20.0)),
),
content: Container(
height: 350,
width: 300,
child: ListView.builder(
itemCount: litems.length,
itemBuilder: (_, index) {
return RaisedButton(
onPressed: () => popupAppuieSurUneFiliale( index)
//changementAffiliation(litems[index]),
child: Text(
litems[index],
style: TextStyle(color: Colors.white),
),
color: Colors.black,
);
}
),
),
actions:[
RaisedButton(
child: Text("Annuler",),
onPressed: (){
Navigator.of(context).pop();
},
color: Colors.blue,
),
]
);
}
);
});
} else(){
};
}
);
Demo
You can copy paste run full code below
You can declare a bool _isOn and set to false and then in onChanged change value
code snippet
bool _isOn = false;
...
Switch(
value: _isOn,
onChanged: (bool isOn) {
setState(() {
_isOn = isOn;
});
working demo
full code
import 'package:flutter/material.dart';
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> {
int _counter = 0;
List<String> litems = ["test"];
bool _isOn = false;
void _incrementCounter() {
setState(() {
_counter++;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Switch(
value: _isOn,
onChanged: (bool isOn) {
setState(() {
_isOn = isOn;
});
if (isOn) {
return showDialog(
context: context,
barrierDismissible: false,
builder: (context) {
return AlertDialog(
//key: _alertDialogKey,
contentPadding:
EdgeInsets.only(left: 25, right: 25),
title:
Center(child: Text("Choisissez une filiale")),
shape: RoundedRectangleBorder(
borderRadius:
BorderRadius.all(Radius.circular(20.0)),
),
content: Container(
height: 350,
width: 300,
child: ListView.builder(
itemCount: litems.length,
itemBuilder: (_, index) {
return RaisedButton(
onPressed: () => null,
//changementAffiliation(litems[index]),
child: Text(
litems[index],
style: TextStyle(color: Colors.white),
),
color: Colors.black,
);
}),
),
actions: [
RaisedButton(
child: Text(
"Annuler",
),
onPressed: () {
Navigator.of(context).pop();
},
color: Colors.blue,
),
]);
});
} else
() {};
}),
Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.headline4,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}