In this application, I'm using TabBar to divide content into categories. Information of each content is coming from a local database. For listing items from that local database to screen, I'm using FutureBuilder and calling some database helpers to fetch data in the future.
As you can see in the screenshot, I have 6 different tabs on top, and in each tab have fetched data with FutureBuilder. (This was what should normally happen.)
I did not encounter with any debug error but only the first FutureBuilder lists the data to the screen. The second tab is not showing anything. (And also the other tabs have the same issue but I just put a placeholder to make code shorter).
Here is the code:
import 'package:cascade_travel_guide/data/database_helper.dart';
import 'package:cascade_travel_guide/model/Categories.dart';
import 'package:cascade_travel_guide/model/HistoricalPlaces.dart';
import 'package:cascade_travel_guide/model/Parks.dart';
import 'package:flutter/material.dart';
class CategoryScreen extends StatefulWidget {
final townid;
CategoryScreen({Key key, this.townid}) : super(key: key);
#override
_CategoryScreenState createState() => _CategoryScreenState(townid: this.townid);
}
class _CategoryScreenState extends State<CategoryScreen> with TickerProviderStateMixin {
var townid;
_CategoryScreenState({this.townid});
var dbHelper = DbHelper();
ScrollController _scrollController;
TabController _tabController;
#override
void initState() {
super.initState();
_tabController = TabController(length: 6, vsync: this);
}
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Theme.of(context).canvasColor,
body: CustomScrollView(
physics: NeverScrollableScrollPhysics(),
controller: _scrollController,
slivers: <Widget>[
SliverAppBar(
bottom: PreferredSize(
child: Container(),
preferredSize: Size(0, 110),
),
pinned: true,
automaticallyImplyLeading: false,
primary: false,
flexibleSpace: Stack(
children: <Widget>[
Container(
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height,
child: Image.network(
"https://i.pinimg.com/originals/6b/47/b9/6b47b97543abed38a12d8c2b4643e870.jpg",
fit: BoxFit.cover,
),
),
Positioned(
child: Column(
children: [
TabBar(
controller: _tabController,
isScrollable: true,
labelColor: Colors.white,
labelPadding: EdgeInsets.symmetric(horizontal: 15),
tabs: [
Tab(text: "Historical Places"),
Tab(text: "Parks"),
Tab(text: "Shopping Malls"),
Tab(text: "Restaurants"),
Tab(text: "Hospitals"),
Tab(text: "Police Stations"),
]),
Container(
decoration: BoxDecoration(
color: Theme.of(context).canvasColor,
borderRadius: BorderRadius.only(
topLeft: Radius.circular(40),
topRight: Radius.circular(40),
),
),
),
],
),
bottom: -1,
left: 0,
right: 0,
),
],
),
),
SliverFillRemaining(
child: Container(
child: TabBarView(
controller: _tabController,
children: [
FutureBuilder<List<Categories>>(
future: dbHelper.getCategories(townid),
builder: (context, snapshot) {
if (snapshot.hasData) {
List catid = [];
for (int i = 0; i < snapshot.data.length; i++) {
if (catid.length < snapshot.data.length)
catid.add(snapshot.data[i].categoriesId);
}
print(catid);
return Container(
child: FutureBuilder<List<HistoricalPlaces>>(
future: dbHelper.getHist(catid[0]),
builder: (context, snapshot) {
if (snapshot.hasData)
return ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(
"${snapshot.data[index].histName}"),
leading: CircleAvatar(),
);
});
else
return Center(
child: CircularProgressIndicator());
}),
);
} else
return Center(
child: CircularProgressIndicator(),
);
}),
FutureBuilder<List<Categories>>(
future: dbHelper.getCategories(townid),
builder: (context, snapshot) {
if (snapshot.hasData) {
List catid = [];
for (int i = 0; i < snapshot.data.length; i++) {
if (catid.length < snapshot.data.length)
catid.add(snapshot.data[i].categoriesId);
}
print(catid);
return Container(
child: FutureBuilder<List<Parks>>(
future: dbHelper.getPark(catid[0]),
builder: (context, snapshot) {
if (snapshot.hasData)
return ListView.builder(
padding: EdgeInsets.zero,
itemCount: snapshot.data.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(
"${snapshot.data[index].parkName}"),
);
});
else
return Center(
child: CircularProgressIndicator());
}),
);
} else
return Center(
child: CircularProgressIndicator(),
);
}),
Center(
child: Text("Test"),
),
Center(
child: Text("Test"),
),
Center(
child: Text("Test"),
),
Center(
child: Text("Test"),
),
],
))),
],
),
);
}
}
P.S.: I also tried to add IndexedStack to fetch all lists together but nothing changed :(
Related
import 'package:flutter/material.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_core/firebase_core.dart';
void main() async {
//Run this first
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'Smart Bin',
home: new HomePageWidget(),
);
}
}
class HomePageWidget extends StatefulWidget {
const HomePageWidget({Key key}) : super(key: key);
#override
_HomePageWidgetState createState() => _HomePageWidgetState();
}
class _HomePageWidgetState extends State<HomePageWidget> {
final scaffoldKey = GlobalKey<ScaffoldState>();
final currentBinRecord = FirebaseFirestore.instance.collection("current_bin");
#override
Widget build(BuildContext context) {
return Scaffold(
key: scaffoldKey,
appBar: AppBar(
title: Text(
'SmartBin',
),
),
body: SafeArea(
child: GestureDetector(
onTap: () => FocusScope.of(context).unfocus(),
child: Column(
mainAxisSize: MainAxisSize.max,
children: [
Expanded(
child: StreamBuilder<List<CurrentBinRecord>>(
stream: queryCurrentBinRecord(
queryBuilder: (currentBinRecord) =>
currentBinRecord.orderBy('level', descending: true),
),
builder: (context, snapshot) {
// Customize what your widget looks like when it's loading.
if (!snapshot.hasData) {
return Center(
child: SizedBox(
width: 50,
height: 50,
child: CircularProgressIndicator(),
),
);
}
List<CurrentBinRecord> listViewCurrentBinRecordList =
snapshot.data;
return ListView.builder(
padding: EdgeInsets.zero,
scrollDirection: Axis.vertical,
itemCount: listViewCurrentBinRecordList.length,
itemBuilder: (context, listViewIndex) {
final listViewCurrentBinRecord =
listViewCurrentBinRecordList[listViewIndex];
return Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Text(
listViewCurrentBinRecord.area,
),
Text(
listViewCurrentBinRecord.level.toString(),
),
],
);
},
);
},
),
),
],
),
),
),
);
}
}
This is the error
First error is on:
child: StreamBuilder<List<CurrentBinRecord>>
The name 'CurrentBinRecord' isn't a type so it can't be used as a type argument.
Try correcting the name to an existing type, or defining a type named 'CurrentBinRecord'.
Second error is on:
stream: queryCurrentBinRecord
The method 'queryCurrentBinRecord' isn't defined for the type '_HomePageWidgetState'.
Try correcting the name to the name of an existing method, or defining a method named 'queryCurrentBinRecord'.
Third error is on:
List<CurrentBinRecord> listViewCurrentBinRecordList =
snapshot.data;
The name 'CurrentBinRecord' isn't a type so it can't be used as a type argument.
Try correcting the name to an existing type, or defining a type named 'CurrentBinRecord'.
These is the syntax try -
return StreamBuilder(
stream: theStreamSource, // Eg a firebase query
builder: (context, AsyncSnapshot snapshot) {
if (!snapshot.hasData) {
return Center(
child: CircularProgressIndicator(),
);
}
return ListView.builder(
itemCount: snapshot.data.documents.length,
itemBuilder: (context, int index) {
return Text(snapshot.data.documents[index]['title']);
}
);
},
);
Hope it helps.
I think the best way to use StreamBuilder is to create separate controller class that can handle all your business logic and update UI.
// your widget class
class UIClass extends StatefulWidget {
const UIClass({Key key}) : super(key: key);
#override
_UIClassState createState() => _UIClassState();
}
class _UIClassState extends State<UIClass> {
UIClassController<List<CurrentBinRecord>> _uiController;
#override
void initState() {
_uiController = UIClassController(StreamController<List<CurrentBinRecord>>());
super.initState();
}
#override
Widget build(BuildContext context) {
return Column(
children: [
Text("Hello Everyone"),
StreamBuilder<List<CurrentBinRecord>>(
stream: _uiController.uiStream,
builder: (context, snapshot) {
if(snapshot.hasError){
return ErrorWidget();
}
else if(snapshot.connectionState == ConnectionState.waiting){
return WaitingWidget();
}
else if(snapshot.hasData){
return ListView.builder(
padding: EdgeInsets.zero,
scrollDirection: Axis.vertical,
itemCount: snapshot.data.length,
itemBuilder: (context, listViewIndex) {
final listViewCurrentBinRecord =
snapshot.data[listViewIndex];
return Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Text(
listViewCurrentBinRecord.area,
),
Text(
listViewCurrentBinRecord.level.toString(),
),
],
);
},
);
}
else{
return SizedBox();
}
}
),
],
);
}
#override
void dispose() {
super.dispose();
_uiController.dispose();
}
}
// controller class to handle all business logic
// you can also split it into multiple sub controllers
class UIClassController<T> {
final StreamController<T> _controller;
// If you are using this on multiple widgets then use asBroadcastStream()
Stream<T> get uiStream => _controller.stream;
UIClassController(this._controller);
void updateMyUI([dynamic params]){
T t;
// your logic //
//------------//
_controller.sink.add(t);
}
void dispose(){
_controller.close();
}
}
Code I'm Using for StreamBuilder
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import '../door/widgets/Navbar.dart';
import '../door/widgets/sidenav.dart';
import 'package:get/get.dart';
FirebaseAuth auth = FirebaseAuth.instance;
class CartPage extends StatefulWidget {
#override
_CartPageState createState() => _CartPageState();
}
class _CartPageState extends State<CartPage> {
final FirebaseFirestore fb = FirebaseFirestore.instance;
int up = 1;
bool loading = false;
final ScrollController _scrollController = ScrollController();
#override
void initState() {
super.initState();
getCartData();
}
void addMore(qty, documentID) {
int new_qty = qty + 1;
Get.snackbar('Qty! ', new_qty.toString());
String collection_name = "cart_${auth.currentUser?.email}";
FirebaseFirestore.instance
.collection(collection_name)
.doc(documentID)
.update({
'qty': new_qty,
'updated_at': Timestamp.now(),
}) // <-- Your data
.then((_) => print('Added'))
.catchError((error) => Get.snackbar('Failed!', 'Error: $error'));
}
void minusMore(qty, documentID) {
if (qty > 1) {
int new_qty = qty - 1;
String collection_name = "cart_${auth.currentUser?.email}";
FirebaseFirestore.instance
.collection(collection_name)
.doc(documentID)
.update({
'qty': new_qty,
'updated_at': Timestamp.now(),
}) // <-- Your data
.then((_) => print('Subtracted'))
.catchError((error) => Get.snackbar('Failed!', 'Error: $error'));
}
}
Stream<QuerySnapshot> getCartData() {
String collection_name = "cart_${auth.currentUser?.email}";
return FirebaseFirestore.instance
.collection(collection_name)
.orderBy("created_at", descending: false)
.snapshots();
}
final ButtonStyle style = ElevatedButton.styleFrom(
minimumSize: Size(50, 30),
backgroundColor: Color(0xFFFFB61A),
elevation: 6,
textStyle: const TextStyle(fontSize: 11),
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.all(
Radius.circular(20),
)));
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: Navbar.navbar(),
drawer: Sidenav.sidenav(),
body: Container(
padding: EdgeInsets.all(10.0),
child: StreamBuilder<QuerySnapshot>(
stream: getCartData(),
builder: (context, snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.waiting:
return Center(child: CircularProgressIndicator());
default:
if (snapshot.hasError) {
return buildText('Something Went Wrong Try later');
} else {
if (!snapshot.hasData) {
return Card(
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
ListTile(
leading: ConstrainedBox(
constraints: const BoxConstraints(
minWidth: 100,
minHeight: 190,
maxWidth: 200,
maxHeight: 200,
),
child: Icon(Icons.shopping_cart, size: 45),
),
title: Text('No Food!'),
subtitle: const Text('Your cart is empty!'),
),
],
),
);
} else {
return ListView.builder(
physics: const BouncingScrollPhysics(),
shrinkWrap: true,
itemCount: snapshot.data?.docs.length,
itemBuilder: (BuildContext context, int index) {
return SizedBox(
height: 90.0,
child: Card(
elevation: 10,
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment:
CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.max,
children: <Widget>[
const SizedBox(
width: 20,
),
_buildImg('assets/logo.png', '60', '60'),
const SizedBox(
width: 14,
),
SizedBox(
width:
MediaQuery.of(context).size.width *
0.33,
child: Column(
mainAxisSize: MainAxisSize.max,
crossAxisAlignment:
CrossAxisAlignment.start,
children: <Widget>[
const SizedBox(
height: 20,
),
Text(
snapshot.data?.docs[index]
["name"],
maxLines: 2,
style: const TextStyle(
fontWeight: FontWeight.w500,
fontSize: 14)),
const SizedBox(
height: 5,
),
Text(
"₹${snapshot.data?.docs[index]["price"]}",
style: const TextStyle(
fontWeight: FontWeight.w400,
fontSize: 12)),
],
),
),
const SizedBox(
width: 10,
),
Container(
margin: const EdgeInsets.only(
top: 20.0, bottom: 10.0),
decoration: BoxDecoration(
color: const Color(0xFFFFB61A),
// color: Color(0xFF0A2031),
borderRadius:
BorderRadius.circular(17)),
child: Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
crossAxisAlignment:
CrossAxisAlignment.end,
children: <Widget>[
SizedBox(
height: 40,
child: IconButton(
onPressed: () {
minusMore(
snapshot.data
?.docs[index]
["qty"],
snapshot
.data
?.docs[index]
.reference
.id);
},
color: Colors.white,
icon:
const Icon(Icons.remove)),
),
const SizedBox(
width: 5,
),
Container(
margin: const EdgeInsets.only(
bottom: 10.0),
child: Text(
"${snapshot.data?.docs[index]["qty"]}",
style: const TextStyle(
fontWeight:
FontWeight.w400,
color: Colors.white,
fontSize: 16)),
),
const SizedBox(
width: 5,
),
SizedBox(
height: 40,
child: IconButton(
color: Colors.white,
onPressed: () {
addMore(
snapshot.data
?.docs[index]
["qty"],
snapshot
.data
?.docs[index]
.reference
.id);
},
icon: const Icon(Icons.add)),
),
],
),
),
const SizedBox(
width: 10,
),
],
),
),
);
});
}
}
}
},
),
),
);
}
Widget buildText(String text) => Center(
child: Text(
text,
style: TextStyle(fontSize: 24, color: Colors.black),
),
);
_buildImg(img, hei, wid) {
return Container(
alignment: Alignment.center, // use aligment
child: Image.asset(
img,
height: double.parse(hei),
width: double.parse(wid),
fit: BoxFit.cover,
),
);
}
}
I have an application:
The listTile items here come from the database.
Codes:
Container(
child: Padding(
padding: EdgeInsets.only(left: 8, right: 8, bottom: 40),
child: Column(
children: [
SizedBox(height: 15,),
Text("Profile", style: TextStyle(fontSize: 27),),
Divider(thickness: 1, color: Colors.black,),
SizedBox(height: 5),
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text("Solved Tests:",style: TextStyle(fontSize: 19)),
],
),
SizedBox(height: 20,),
Container(
width: double.infinity,
height: 200,
child: Expanded(
child: FutureBuilder(
future: listUpload(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
late List<String?> items;
if (snapshot.connectionState == ConnectionState.waiting) {
items = [];
} else if (snapshot.connectionState == ConnectionState.done &&
snapshot.hasData) {
items = snapshot.data as List<String?>;
} else {
items = [];
}
return Scrollbar(
isAlwaysShown: true,
controller: _scrollContreller,
scrollbarOrientation: ScrollbarOrientation.right,
child: ListView.builder(
controller: _scrollContreller,
itemCount: items.length,
itemBuilder: (BuildContext context, int index) {
return Padding(
padding: const EdgeInsets.only(bottom: 20, left: 10, right: 10),
child: Container(
decoration: BoxDecoration(
color: Colors.grey[300],
borderRadius: BorderRadius.circular(10),
),
child: ListTile(
title: Text(
items[index].toString(),
style: TextStyle(fontSize: 20),
),
),
),
);
},
),
);
})),
),
Calling items from database:
dynamic listUpload() async {
final prefences = await SharedPreferences.getInstance();
final getTests = prefences.getStringList("tests"); // get item
debugPrint(getTests.toString());
return Future.value(getTests);
}
If there is no data in the database, I want the listTile to say "not found" in its footprint.
For example, I want to make the system in the picture above. You already understand the system, if there is no data from the database, listTile will say not found in its place.
The listTile in my app looks like this when there is no data:
How can I do that? Thanks in advance for the help.
I think you can just have a check for empty data.
if (!snapshot.hasData){
return myEmptyWidget
}
please try:
items.isEmpty ? const SizedBox() : View(),
Code:
Container(
child: Padding(
padding: EdgeInsets.only(left: 8, right: 8, bottom: 40),
child: Column(
children: [
SizedBox(height: 15,),
Text("Profile", style: TextStyle(fontSize: 27),),
Divider(thickness: 1, color: Colors.black,),
SizedBox(height: 5),
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text("Solved Tests:",style: TextStyle(fontSize: 19)),
],
),
SizedBox(height: 20,),
Container(
width: double.infinity,
height: 200,
child: Expanded(
child: FutureBuilder(
future: listUpload(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
late List<String?> items;
if (snapshot.connectionState == ConnectionState.waiting) {
items = [];
} else if (snapshot.connectionState == ConnectionState.done &&
snapshot.hasData) {
items = snapshot.data as List<String?>;
} else {
items = [];
}
return Scrollbar(
isAlwaysShown: true,
controller: _scrollContreller,
scrollbarOrientation: ScrollbarOrientation.right,
child: items.isEmpty ? const SizedBox() : ListView.builder(
controller: _scrollContreller,
itemCount: items.length,
itemBuilder: (BuildContext context, int index) {
return Padding(
padding: const EdgeInsets.only(bottom: 20, left: 10, right: 10),
child: Container(
decoration: BoxDecoration(
color: Colors.grey[300],
borderRadius: BorderRadius.circular(10),
),
child: ListTile(
title: Text(
items[index].toString(),
style: TextStyle(fontSize: 20),
),
),
),
);
},
),
);
})),
),
You're facing this issue because you're not fully using the power of the FutureBuilder !
It'll "emit" states during Load and Success/Error. Whenever a state is emitted, it'll redraw the widget.
Therefore, you can return a Widget matching every state of your FutureBuilder like this
...
child: FutureBuilder(
future: listUpload(),
builder:
(BuildContext context, AsyncSnapshot snapshot) {
late List<String?> items;
if (snapshot.connectionState ==
ConnectionState.waiting) {
return YourLoaderWidget();
} else if (snapshot.connectionState ==
ConnectionState.done &&
snapshot.hasData) {
items = snapshot.data as List<String?>;
if (items.isEmpty) {
return YourEmptyWidget();
} else {
return YourScrollBar();
}
} else {
return YourErrorWidget();
}
...
I have been learning to make an app in flutter for 2 weeks using flutter documentation. Today, I have come to this problem regarding Navigator.push(). This was working fine, but it is not after I restart my laptop. And I can't find a solution, I tried
flutter clean
reinstalled flutter and dart plugins
These solutions didn't help.
Code:
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter_foodybite/screens/trending.dart';
import 'package:flutter_foodybite/util/categories.dart';
import 'package:flutter_foodybite/util/friends.dart';
import 'package:flutter_foodybite/util/restaurants.dart';
import 'package:flutter_foodybite/widgets/category_item.dart';
import 'package:flutter_foodybite/widgets/search_card.dart';
import 'package:flutter_foodybite/widgets/slide_item.dart';
class Home extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: buildSearchBar(context),
body: Padding(
padding: EdgeInsets.fromLTRB(10.0, 0, 10.0, 0),
child: ListView(
children: <Widget>[
SizedBox(height: 20.0),
buildCategoryRow('Trending Restaurants', context),
SizedBox(height: 10.0),
buildRestaurantList(context),
SizedBox(height: 10.0),
buildCategoryRow('Category', context),
SizedBox(height: 10.0),
buildCategoryList(context),
SizedBox(height: 20.0),
buildCategoryRow('Friends', context),
SizedBox(height: 10.0),
buildFriendsList(),
SizedBox(height: 30.0),
],
),
),
);
}
buildCategoryRow(String category, BuildContext context) {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Text(
"$category",
style: TextStyle(
fontSize: 20.0,
fontWeight: FontWeight.w800,
),
),
FlatButton(
child: Text(
"Trending",
style: TextStyle(
color: Theme.of(context).accentColor,
),
),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (BuildContext context) {
return Trending();
},
),
);
},
),
],
);
}
buildSearchBar(BuildContext context) {
return PreferredSize(
child: Padding(
padding: EdgeInsets.only(
top: Platform.isAndroid ? 30.0 : 50.0,
left: 10.0,
right: 10.0,
),
child: SearchCard(),
),
preferredSize: Size(
MediaQuery.of(context).size.width,
60.0,
),
);
}
buildCategoryList(BuildContext context) {
return Container(
height: MediaQuery.of(context).size.height / 6,
child: ListView.builder(
primary: false,
scrollDirection: Axis.horizontal,
shrinkWrap: true,
itemCount: categories == null ? 0 : categories.length,
itemBuilder: (BuildContext context, int index) {
Map cat = categories[index];
return CategoryItem(
cat: cat,
);
},
),
);
}
buildRestaurantList(BuildContext context) {
return Container(
height: MediaQuery.of(context).size.height / 2.4,
width: MediaQuery.of(context).size.width,
child: ListView.builder(
primary: false,
shrinkWrap: true,
scrollDirection: Axis.horizontal,
itemCount: restaurants == null ? 0 : restaurants.length,
itemBuilder: (BuildContext context, int index) {
Map restaurant = restaurants[index];
return Padding(
padding: EdgeInsets.only(right: 10.0),
child: SlideItem(
img: restaurant["img"],
title: restaurant["title"],
address: restaurant["address"],
rating: restaurant["rating"],
),
);
},
),
);
}
buildFriendsList() {
return Container(
height: 50.0,
child: ListView.builder(
primary: false,
scrollDirection: Axis.horizontal,
shrinkWrap: true,
itemCount: friends == null ? 0 : friends.length,
itemBuilder: (BuildContext context, int index) {
String img = friends[index];
return Padding(
padding: EdgeInsets.only(right: 5.0),
child: CircleAvatar(
backgroundImage: AssetImage(
img,
),
radius: 25.0,
),
);
},
),
);
}
}
Please have a look at the attached picture below for the highlighted error.
Here is error encountered
This is an error
Try this
flutter upgrade --force
You should use like this
Navigator.of(context)
.push(
MaterialPageRoute(
builder: (BuildContext context) => Trending(),
),
);
I am using provider for flutter state management. Below is my code
Home
class HomePage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return DefaultTabController(
length: 2,
child: Scaffold(
appBar: AppBar(
//backgroundColor: Color.fromRGBO(0, 72, 100, 10),
backgroundColor: Color.fromRGBO(25, 72, 114, 10),
title: Text("something"),
bottom: TabBar(
indicatorColor: Colors.white70,
labelStyle: TextStyle(
fontFamily: 'Roboto-Regular',
fontSize: 16.0,
letterSpacing: 0.15),
labelColor: Colors.white,
labelPadding: EdgeInsets.all(5),
tabs: <Widget>[
Tab(
text: "Fresh Products",
),
Tab(
text: "Frozen Products",
),
],
),
),
body: Center(child: Home()),
),
);
}
}
class Home extends StatelessWidget {
#override
Widget build(BuildContext context) {
print("Home build methof");
return TabBarView(
children: <Widget>[
Container(
height: double.infinity,
child: FutureBuilder(
future: Provider.of<ProductSpeciesImpl>(context, listen: false)
.getAllSpecies(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return Center(
child: Container(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
CircularProgressIndicator(),
Container(
height: 10,
),
Text(
"Loading Data... Please Wait",
style: Theme.of(context).textTheme.body1,
)
],
),
),
);
} else {
if (snapshot.hasError) {
return Center(
child: Text("An error Occured"),
);
} else {
return Consumer<ProductSpeciesImpl>(
builder: (context, data, child) => GridView.builder(
physics:
ScrollPhysics(), // to disable GridView's scrolling
shrinkWrap: true,
itemCount: Provider.of<ProductSpeciesImpl>(context)
.productList
.length,
gridDelegate:
new SliverGridDelegateWithFixedCrossAxisCount(
childAspectRatio:
(MediaQuery.of(context).size.width *
.5 /
190),
crossAxisCount: 2),
itemBuilder: (BuildContext context, int index) {
return GestureDetector(
onTap: () {
//print("sdsdsdsd");
// print(snapshot.data[index].name + " " + snapshot.data[index].idProductSpecies.toString() + " "+ snapshot.data[index].photo);
Navigator.pushNamed(context, "/products",
arguments: {
"name": Provider.of<ProductSpeciesImpl>(
context,
listen: false)
.productList[index]
.name,
"id": Provider.of<ProductSpeciesImpl>(
context,
listen: false)
.productList[index]
.idProductSpecies,
"photo": Provider.of<ProductSpeciesImpl>(
context,
listen: false)
.productList[index]
.photo
});
},
child: Card(
elevation: 4.0,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8.0)),
clipBehavior: Clip.antiAlias,
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Container(
child: Text(
Provider.of<ProductSpeciesImpl>(context,
listen: false)
.productList[index]
.name,
),
)
],
),
),
);
}));
}
}
},
),
),
ProductSpeciesImpl
class ProductSpeciesImpl
with ChangeNotifier
implements ProductSpeciesInterface {
NavLinks navLinks = NavLinks();
List<ProductSpecies> productList = [];
#override
Future<void> getAllSpecies() async {
var data = await http.get(navLinks.getAllProductSpecies());
var jsonData = convert.json.decode(data.body).cast<Map<String, dynamic>>();
try {
productList = jsonData
.map<ProductSpecies>((json) => new ProductSpecies.fromJson(json))
.toList();
print("Product sIZE: " + productList.length.toString());
notifyListeners();
} catch (error) {
throw error;
}
}
}
The code works fine. The issue is every time I visit another page and come back to this page, the UI gets reloaded. I have used consumer, as far as I understood, consumer will only load the relevant part when it is called. That means I don't have to run my product loading code inside init as well. So, I do not understand why this is happening.
Appreciate your support to fix this issue.
You are initiating an HTTP request in your build() function here by calling getAllSpecies(). You are not supposed to do that.
class Home extends StatelessWidget {
#override
Widget build(BuildContext context) {
//...
child: FutureBuilder(
future: Provider.of<ProductSpeciesImpl>(context, listen: false)
.getAllSpecies(),
build() functions are supposed to have no side effects. Please convert the widget to StatefulWidget and do your loading in initState(). Alternatively, make a parent StatefulWidget do the loading in its initState() and hand the data to this widget in its constructor.
Currently I'm using flutter package 'Reorderables' to show a reorderable listview which contains several images.These images can be deleted from listview through a button , everything works fine. But the listview rebuild everytime when I delete an image. I'm using a class called 'ReorderableListviewManager' with ChangeNotifier to update images and Provider.of<ReorderableListviewManager>(context) to get latest images . The problem now is that using Provider.of<ReorderableListviewManager>(context) makes build() called everytime I delete an image , so the listview rebuid. I koow I
can use consumer to only rebuild part of widget tree, but it seems like that there's no place to put consumer in children of this Listview. Is there a way to rebuild only image but not whole ReorderableListview ? Thanks very much!
Below is my code:
class NotePicturesEditScreen extends StatefulWidget {
final List<Page> notePictures;
final NotePicturesEditBloc bloc;
NotePicturesEditScreen({#required this.notePictures, #required this.bloc});
static Widget create(BuildContext context, List<Page> notePictures) {
return Provider<NotePicturesEditBloc>(
create: (context) => NotePicturesEditBloc(),
child: Consumer<NotePicturesEditBloc>(
builder: (context, bloc, _) =>
ChangeNotifierProvider<ReorderableListviewManager>(
create: (context) => ReorderableListviewManager(),
child: NotePicturesEditScreen(
bloc: bloc,
notePictures: notePictures,
),
)),
dispose: (context, bloc) => bloc.dispose(),
);
}
#override
_NotePicturesEditScreenState createState() => _NotePicturesEditScreenState();
}
class _NotePicturesEditScreenState extends State<NotePicturesEditScreen> {
PreloadPageController _pageController;
ScrollController _reorderableScrollController;
List<Page> notePicturesCopy;
int longPressIndex;
List<double> smallImagesWidth;
double scrollOffset = 0;
_reorderableScrollListener() {
scrollOffset = _reorderableScrollController.offset;
}
#override
void initState() {
Provider.of<ReorderableListviewManager>(context, listen: false)
.notePictures = widget.notePictures;
notePicturesCopy = widget.notePictures;
_reorderableScrollController = ScrollController();
_pageController = PreloadPageController();
_reorderableScrollController.addListener(_reorderableScrollListener);
Provider.of<ReorderableListviewManager>(context, listen: false)
.getSmallImagesWidth(notePicturesCopy, context)
.then((imagesWidth) {
smallImagesWidth = imagesWidth;
});
super.initState();
}
#override
void dispose() {
_pageController.dispose();
_reorderableScrollController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
ReorderableListviewManager reorderableManager =
Provider.of<ReorderableListviewManager>(context, listen: false);
return SafeArea(
child: Scaffold(
appBar: AppBar(
backgroundColor: Colors.white,
shape: Border(bottom: BorderSide(color: Colors.black12)),
iconTheme: IconThemeData(color: Colors.black87),
elevation: 0,
automaticallyImplyLeading: false,
titleSpacing: 0,
centerTitle: true,
title: Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Container(
child: IconButton(
padding: EdgeInsets.only(left: 20, right: 12),
onPressed: () => Navigator.of(context).pop(),
icon: Icon(Icons.close),
),
),
Text('編輯',
style: TextStyle(color: Colors.black87, fontSize: 18))
],
),
actions: <Widget>[
FlatButton(
onPressed: () {},
child: Text(
'下一步',
),
)
],
),
backgroundColor: Color(0xffeeeeee),
body: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Spacer(),
StreamBuilder<List<Page>>(
initialData: widget.notePictures,
stream: widget.bloc.notePicturesStream,
builder: (context, snapshot) {
notePicturesCopy = snapshot.data;
return Container(
margin: EdgeInsets.symmetric(horizontal: 20),
height: MediaQuery.of(context).size.height * 0.65,
child: PreloadPageView.builder(
preloadPagesCount: snapshot.data.length,
controller: _pageController,
itemCount: snapshot.data.length,
onPageChanged: (index) {
reorderableManager.updateCurrentIndex(index);
reorderableManager.scrollToCenter(
smallImagesWidth,
index,
scrollOffset,
_reorderableScrollController,
context);
},
itemBuilder: (context, index) {
return Container(
child: Image.memory(
File.fromUri(
snapshot.data[index].polygon.isNotEmpty
? snapshot.data[index]
.documentPreviewImageFileUri
: snapshot.data[index]
.originalPreviewImageFileUri)
.readAsBytesSync(),
gaplessPlayback: true,
alignment: Alignment.center,
),
);
}),
);
},
),
Spacer(),
Container(
height: MediaQuery.of(context).size.height * 0.1,
margin: EdgeInsets.symmetric(horizontal: 20),
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: ReorderableRow(
scrollController: _reorderableScrollController,
buildDraggableFeedback: (context, constraints, __) =>
Container(
width: constraints.maxWidth,
height: constraints.maxHeight,
child: Image.memory(File.fromUri(
notePicturesCopy[longPressIndex]
.polygon
.isNotEmpty
? notePicturesCopy[longPressIndex]
.documentPreviewImageFileUri
: notePicturesCopy[longPressIndex]
.originalPreviewImageFileUri)
.readAsBytesSync()),
),
onReorder: (oldIndex, newIndex) async {
List<Page> result = await widget.bloc.reorderPictures(
oldIndex,
newIndex,
reorderableManager.notePictures);
_pageController.jumpToPage(newIndex);
reorderableManager.updateNotePictures(result);
reorderableManager
.getSmallImagesWidth(result, context)
.then((imagesWidth) {
smallImagesWidth = imagesWidth;
});
},
footer: Container(
width: 32,
height: 32,
margin: EdgeInsets.only(left: 16),
child: SizedBox(
child: FloatingActionButton(
backgroundColor: Colors.white,
elevation: 1,
disabledElevation: 0,
highlightElevation: 1,
child: Icon(Icons.add, color: Colors.blueAccent),
onPressed: notePicturesCopy.length >= 20
? () {
Scaffold.of(context)
.showSnackBar(SnackBar(
content: Text('筆記上限為20頁 !'),
));
}
: () async {
List<Page> notePictures =
await widget.bloc.addPicture(
reorderableManager.notePictures);
List<double> imagesWidth =
await reorderableManager
.getSmallImagesWidth(
notePictures, context);
smallImagesWidth = imagesWidth;
reorderableManager.updateCurrentIndex(
notePictures.length - 1);
reorderableManager
.updateNotePictures(notePictures);
_pageController
.jumpToPage(notePictures.length - 1);
},
),
),
),
children: Provider.of<ReorderableListviewManager>(
context)
.notePictures
.asMap()
.map((index, page) {
return MapEntry(
index,
Consumer<ReorderableListviewManager>(
key: ValueKey('value$index'),
builder: (context, manager, _) =>
GestureDetector(
onTapDown: (_) {
longPressIndex = index;
},
onTap: () {
reorderableManager.scrollToCenter(
smallImagesWidth,
index,
scrollOffset,
_reorderableScrollController,
context);
_pageController.jumpToPage(index);
},
child: Container(
margin: EdgeInsets.only(
left: index == 0 ? 0 : 12),
decoration: BoxDecoration(
border: Border.all(
width: 1.5,
color: index ==
manager
.getCurrentIndex
? Colors.blueAccent
: Colors.transparent)),
child: index + 1 <=
manager.notePictures.length
? Image.memory(
File.fromUri(manager
.notePictures[
index]
.polygon
.isNotEmpty
? manager
.notePictures[
index]
.documentPreviewImageFileUri
: manager
.notePictures[
index]
.originalPreviewImageFileUri)
.readAsBytesSync(),
gaplessPlayback: true,
)
: null),
),
));
})
.values
.toList()),
)),
Spacer(),
Container(
decoration: BoxDecoration(
color: Colors.white,
border: Border(top: BorderSide(color: Colors.black12))),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
FlatButton(
onPressed: () async => await widget.bloc
.cropNotePicture(reorderableManager.notePictures,
_pageController.page.round())
.then((notePictures) {
reorderableManager.updateNotePictures(notePictures);
reorderableManager
.getSmallImagesWidth(notePictures, context)
.then((imagesWidth) {
smallImagesWidth = imagesWidth;
});
}),
child: Column(
children: <Widget>[
Icon(
Icons.crop,
color: Colors.blueAccent,
),
Container(
margin: EdgeInsets.only(top: 1),
child: Text(
'裁切',
style: TextStyle(color: Colors.blueAccent),
),
)
],
),
),
FlatButton(
onPressed: () {
int deleteIndex = _pageController.page.round();
widget.bloc
.deletePicture(
reorderableManager.notePictures, deleteIndex)
.then((notePictures) {
if (deleteIndex == notePictures.length) {
reorderableManager
.updateCurrentIndex(notePictures.length - 1);
}
reorderableManager.updateNotePictures(notePictures);
reorderableManager
.getSmallImagesWidth(notePictures, context)
.then((imagesWidth) {
smallImagesWidth = imagesWidth;
});
if (reorderableManager.notePictures.length == 0) {
Navigator.pop(context);
}
});
},
child: Column(
children: <Widget>[
Icon(
Icons.delete_outline,
color: Colors.blueAccent,
),
Container(
margin: EdgeInsets.only(top: 1),
child: Text(
'刪除',
style: TextStyle(color: Colors.blueAccent),
),
),
],
),
)
],
),
)
],
)),
);
}
}
You can't prevent a rebuild on your ReorderableListView widget because it will be rebuild every time there's an update on the Provider. What you can do here is to keep track the current index of all visible ListView items. When new data should be displayed coming from the Provider, you can retain the current indices of previous ListView items, and add the newly added items at the end of the list, or wherever you like.