Related
I am Building a flutter e-commerce application. And in the Categories section. There are so much data so I want to implement pagination through infinite scrolling.
This is what I am trying to do, CatHome object is the category either men or women etc.
the code is resulting in pagination, but everytime it is building another page, not infinite scroll. so i cannot access previosly loaded data
class CategoryDetail extends StatefulWidget {
CatHome catHome;
CategoryDetail(this.catHome);
#override
_CategoryDetailstate createState() => _CategoryDetailstate(catHome);
}
class _CategoryDetailstate extends State<CategoryDetail> {
List<FutureOr> _items = [];
CatHome catHome;
_CategoryDetailstate(this.catHome);
var _currentPage = 1;
ScrollController scroll = ScrollController();
var _hasMore = true;
#override
void initState() {
super.initState();
scroll.addListener(() {
if (scroll.position.pixels == scroll.position.maxScrollExtent) {
_fetchMore();
}
});
}
#override
void dispose() {
scroll.dispose();
super.dispose();
}
void _fetchMore() async {
if (_hasMore) {
setState(() {
_currentPage++;
});
var newItems =
await new catitems_api().fetchdeails(catHome, _currentPage);
if (newItems.isEmpty) {
setState(() {
_hasMore = false;
});
} else {
setState(() {
this._items.addAll(newItems);
});
}
}
}
/// Component widget in flashSale layout
Widget build(BuildContext context) {
var data = EasyLocalizationProvider.of(context).data;
return EasyLocalizationProvider(
data: data,
child: Scaffold(
appBar: AppBar(
backgroundColor: Colors.white,
title: Text(
AppLocalizations.of(context).tr('Category'),
style: TextStyle(
fontWeight: FontWeight.w700,
fontSize: 17.0,
color: Colors.black54,
fontFamily: "Gotik"),
),
centerTitle: true,
iconTheme: IconThemeData(color: Color(0xFF6991C7)),
elevation: 0.0,
),
body: SingleChildScrollView(
child: Container(
child: Column(
children: <Widget>[
Container(
key: Key(UniqueKey().toString()),
height: MediaQuery.of(context).size.height * 0.8,
child: FutureBuilder<List<dynamic>>(
future: catitems_api().fetchdeails(catHome, _currentPage),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return Center(
child: CircularProgressIndicator(),
);
}
return GridView.builder(
controller: scroll,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
childAspectRatio: 0.7,
mainAxisSpacing: 8.0,
crossAxisSpacing: 8.0,
),
itemCount: snapshot.data.length,
itemBuilder: (BuildContext context, int index) {
CategoryItems items = snapshot.data[index];
return InkWell(
onTap: () {
new productDet_api()
.fetchdeails(items.id)
.then((itemList) {
Navigator.of(context).push(PageRouteBuilder(
pageBuilder: (_, __, ___) =>
new detailProduk(itemList.first),
transitionDuration:
Duration(milliseconds: 950),
transitionsBuilder: (_flashSaleState,
Animation<double> animation,
__,
Widget child) {
return Opacity(
opacity: animation.value,
child: child,
);
}));
});
},
child: Container(
decoration: BoxDecoration(
color: Colors.white,
boxShadow: [
BoxShadow(
color: Colors.black12.withOpacity(0.1),
spreadRadius: 0.2,
blurRadius: 5.0,
offset: Offset(0.0, 2.0))
]),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Image.network(
items.image,
width: 150.0,
height: 150.0,
fit: BoxFit.cover,
),
SizedBox(
height: 10.0,
),
Text(
items.name,
style: TextStyle(
fontWeight: FontWeight.w600,
fontSize: 14.0,
color: Colors.black),
),
SizedBox(
height: 5.0,
),
Text(
"\$" + items.price.toString(),
style: TextStyle(
fontWeight: FontWeight.w600,
fontSize: 14.0,
color: Colors.black),
),
],
),
),
);
},
);
},
),
),
],
),
),
),
),
);
}
}
I am getting that infamous error, which I am not able to resolve.
RenderBox was not laid out: RenderRepaintBoundary#03a24 relayoutBoundary=up14 NEEDS-PAINT
'package:flutter/src/rendering/box.dart':
Failed assertion: line 2001 pos 12: 'hasSize
I tried all sorts of wrapping of widgets. The error is on the Container(). In that container I am trying to display 10 newest products, scrolled horizontaly, and fetched from the firestore database. Below, I display list of outlets, scrolled vertically and they do not cause any problem, neither the horizontal scrolling widget, which displays categories.
I have a tabscreen and in the TabView I have this class:
class UserHomeMerch extends StatefulWidget {
UserHomeMerch({Key? key}) : super(key: key);
#override
UserHomeMerchState createState() => UserHomeMerchState();
}
Class UserHomeMerchState extends State<UserHomeMerch> {
#override
Widget build(BuildContext context) {
final outletData = Provider.of<OutletData>(context);
return GestureDetector(
onTap: () {
FocusScope.of(context).unfocus();
},
child: Scaffold(
body: Stack(
children: <Widget>[
Container(
decoration: const BoxDecoration(
image: DecorationImage(image: AssetImage("assets/images/background.jpg"),
fit: BoxFit.cover,),
),
),
FutureBuilder(
future: outletData.getAllOutlets(context),
builder: (ctx, AsyncSnapshot snapshot) {
if (snapshot.connectionState == ConnectionState.waiting ||
snapshot.data == null) {
return Center(
child: CircularProgressIndicator(color: color4),
);
}
var sd = snapshot.data;
return SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.all(10.0),
child: Text(
Languages.of(context)!.category+':',
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 21,
color: color1,
),
),
),
Categories(),
Padding(
padding: const EdgeInsets.all(10.0),
child: Text(
Languages.of(context)!.newProducts,
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 21,
color: color1,
),
),
),
Container( =====> this is the error line
height: 250,
child: StreamBuilder<QuerySnapshot>(
stream: productsRef
.orderBy('timestamp', descending: true)
.limit(10)
.snapshots(),
builder: (context, snapshot) {
return (snapshot.connectionState == ConnectionState.waiting)
? Center(child: CircularProgressIndicator(color: color4,))
: ListView.builder(
physics: NeverScrollableScrollPhysics(),
shrinkWrap: true,
scrollDirection: Axis.horizontal,
itemCount: snapshot.data!.docs.length,
itemBuilder: (context, index) {
DocumentSnapshot data = snapshot.data!.docs[index];
return ProductItem(
productId: data[productId],
productName: data[productName],
productDescription: data[prodDesc],
productPrice: data[productPrice],
count: data[countInStock],
images: data['productImagesUrl'],
outletID: data[outletId],
merchId: data[merchantId]
);
});
},
)),
Padding(
padding: const EdgeInsets.all(10.0),
child: Text(
Languages.of(context)!.producers+':',
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 21,
color: color1,
),
),
),
sd.length == 0
? Center(
child: Text(Languages.of(context)!.noOrg),
)
: ListView.builder(
padding: EdgeInsets.zero,
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
itemBuilder: (ctx, index) {
return OutletItem(
outletName: sd[index][outletName],
id: sd[index][outletId],
category: sd[index][category],
outletImage: sd[index][outletImg],
outletRating: sd[index][outletRating],
merchId: sd[index][merchantId],
);
},
itemCount: sd.length,
),
],
),
);
},
),
]),
));
}
}
My productItem widget is a card, with the tap displays bottomModal, here it is:
class ProductItem extends StatelessWidget {
final String productId;
final String productName;
final String productDescription;
final num productPrice;
final num count;
final List<dynamic> images;
final String outletID, merchId;
ProductItem(
{required this.productId,
required this.productName,
required this.productDescription,
required this.productPrice,
required this.count,
required this.images,
required this.outletID,
required this.merchId});
#override
Widget build(BuildContext context) {
return Padding(
padding: EdgeInsets.symmetric(horizontal: 20),
child: Card(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(30)),
elevation: 20,
color: color2.withOpacity(0.5),
shadowColor: color2.withOpacity(0.2),
child: Column(
children: [
Container(
alignment: Alignment.center,
child: Image.network(
images.first,
//height: 500,
//width: 500,
fit: BoxFit.cover,
),
height: 250,
//width: 100,
),
ListTile(
selectedColor: Colors.white70,
title: Text(
productName,
overflow: TextOverflow.fade,
softWrap: true,
style: TextStyle(
fontWeight: FontWeight.bold,
),
),
subtitle: Text(
"₮ ${NumberFormat.decimalPattern().format(productPrice)}",
),
trailing: TextButton.icon(
onPressed: () {
showModal(context, productId, productName, productDescription, productPrice, images, count, outletID, merchId);
},
icon: Icon(
Icons.add,
color: Colors.black54,
),
label: Text(
Languages.of(context)!.seeProf,
),
style: ButtonStyle(
shape: MaterialStateProperty.all<RoundedRectangleBorder>(
RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10),
side: BorderSide(
color: Colors.black54,
),
),
),
),
),
),
Divider(thickness: 2),
],
),
),
);
}
}
Please, help me fix this issue. Thank you very much in advance!
I think the error is on the second listview builder. try wrapping it with an Expanded widget. if not and you are 100% sure on it's on the horizontal listview builder try giving the container a width and wrapping the listview builder with an Expanded Widget.
I hope this helps
My code includes FutureBuilder(), which get data from Firestore, and its child widgets include GridView.builder and TextField widgets etc.
When I click on a TexField(focus), the codes in FutureBuilder are rebuild.
The following is the test code for this.
Can you tell me the cause and solution of this problem?
class TestRoom extends StatefulWidget {
TestRoom({Key? key}) : super(key: key);
#override
State<TestRoom> createState() => _TestRoomState();
}
class _TestRoomState extends State<TestRoom> {
List<RoomModel> _roomModels = [];
TextEditingController _textEditingController = TextEditingController();
bool _isTablet = false;
#override
void dispose() {
_textEditingController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
Size size = MediaQuery.of(context).size;
if (size.width >= 800) {
_isTablet = true;
} else {
_isTablet = false;
}
return Scaffold(
appBar: AppBar(),
body: Padding(
padding: EdgeInsets.all(20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
"Test",
style: TextStyle(
fontSize: 35,
fontWeight: FontWeight.bold,
),
),
SizedBox(
height: 20,
),
FutureBuilder(
future: _getAllRoom(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const Center(
child: CircularProgressIndicator(),
);
}
return Expanded(
child: TestList1(
isTablet: _isTablet,
roomModels: _roomModels,
isListStyle1: true,
));
},
),
],
),
),
);
}
// get user's models from firestore
Future _getAllRoom() async {
_roomModels.clear();
_roomModels.addAll(await RoomService().getAllRoomModel("userName"));
}
}
//
//
class TestList1 extends StatefulWidget {
final isTablet;
final List<RoomModel> roomModels;
final bool isListStyle1;
TestList1({
Key? key,
required this.isTablet,
required this.roomModels,
required this.isListStyle1,
}) : super(key: key);
#override
State<TestList1> createState() => _TestList1State();
}
class _TestList1State extends State<TestList1> {
TextEditingController _textEditingController = TextEditingController();
double _paddingSize = 40.0;
#override
void dispose() {
// _textEditingController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return GridView.builder(
shrinkWrap: true,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: widget.isTablet ? 6 : 3,
childAspectRatio: 1 / 1.5,
mainAxisSpacing: _paddingSize,
crossAxisSpacing: 10,
),
itemBuilder: (context, index) {
return _buildMyRooms(widget.roomModels[index], index);
},
itemCount: widget.roomModels.length,
);
}
Widget _buildMyRooms(RoomModel roomModel, int index) {
return Column(
children: [
InkWell(
onTap: () {},
child: Container(
width: 120,
height: 168,
decoration: BoxDecoration(
color: Colors.white,
border: Border.all(
color: Colors.blue,
width: 2.0,
),
borderRadius: const BorderRadius.all(
Radius.circular(10),
),
),
),
),
SizedBox(
height: sm_padding,
),
PopupMenuButton<int>(
color: Colors.grey[100],
itemBuilder: (context) => _fileMenuItemLust(roomModel),
onSelected: _onSeletedFileMenu,
child: Column(
children: [
Container(
width: 130,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Flexible(
child: Text(
roomModel.roomName,
overflow: TextOverflow.ellipsis,
maxLines: 1,
style: const TextStyle(
color: Colors.blue,
fontSize: 15,
),
),
),
const Icon(
Icons.arrow_drop_down_outlined,
color: Colors.blue,
),
],
),
),
],
),
)
],
);
}
// 파일 메뉴 아이템
List<PopupMenuEntry<int>> _fileMenuItemLust(RoomModel roomModel) {
_textEditingController.text = roomModel.roomName;
return [
// 파일명
PopupMenuItem(
enabled: false,
// TODO textfield
child: TextField(
controller: _textEditingController,
style: const TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
),
maxLines: 1,
decoration: InputDecoration(
border: _textFieldBorder(),
enabledBorder: _textFieldBorder(),
disabledBorder: _textFieldBorder(),
focusedBorder: _textFieldBorder(),
focusColor: Colors.white60,
filled: true,
fillColor: Colors.grey.withOpacity(0.3),
isDense: true, // padding 조절을 위해 추가
contentPadding: EdgeInsets.all(sm_padding),
)),
),
const PopupMenuDivider(),
PopupMenuItem(
value: 0,
child: Row(
children: [
const Icon(
Icons.copy,
),
SizedBox(
width: sm_padding,
),
const Text(
"Menu Item1",
),
],
),
),
];
}
void _onSeletedFileMenu(value) {}
OutlineInputBorder _textFieldBorder() {
return OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(20)),
borderSide: BorderSide(
color: Colors.grey.withOpacity(0.3),
width: 1,
),
);
}
}
// ======================
I modified the code based on Yeasin Sheikh's answer.
The existing problem has been solved, but a new problem has arisen.
I'm going to update the Firestore using onSubmitted() of TextField and then update my list using stream builder and future builder.
This is a modified part of the code above to solve an existing problem
class _TestRoomState extends State<TestRoom> {
late final myFuture = _getAllRoom(); // new
FutureBuilder(
future: myFuture, //_getAllRoom(), // change
The problem of constantly rebuilding the text field according to the focus has disappeared, but there is a problem that getAllRoom() is called only at the first time and cannot be called afterwards, so the new room list cannot be updated.
Here future: _getAllRoom() calls the api on every state changes.
Create a state variable for future
late final myFuture = _getAllRoom();
#override
Widget build(BuildContext context) {
And use
FutureBuilder(
future:myFuture ,
You can check Fixing a common FutureBuilder and StreamBuilder problem
class SupportScreen extends StatefulWidget {
const SupportScreen({Key? key}) : super(key: key);
#override
State<SupportScreen> createState() => _SupportScreenState();
}
class _SupportScreenState extends State<SupportScreen> {
final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
final ScrollController _scrollController = ScrollController();
int? selected;
int page = 1;
#override
void initState() {
super.initState();
updateState();
}
updateState() {
_scrollController.addListener(() {
if (_scrollController.position.pixels >=
_scrollController.position.maxScrollExtent) {
setState(() {
page = page + 1;
});
BlocProvider.of<SupportBloc>(_scaffoldKey.currentState!.context)
.add(SupportGetFaqsWithPagination(page));
}
});
}
#override
void dispose() {
super.dispose();
_scrollController.dispose();
}
#override
Widget build(BuildContext context) {
return BlocProvider(
create: (context) => SupportBloc(
SupportService(),
)..add(
SupportGetFaqsWithPagination(1),
),
child: Scaffold(
key: _scaffoldKey,
body: SafeArea(
child: Padding(
padding: const EdgeInsets.only(
top: 16, bottom: 0, left: 16, right: 16),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
title(),
const SizedBox(height: 20),
BlocConsumer<SupportBloc, SupportState>(
listener: (context, state) {
if (state is SupportError) {
if (state.message.toString() == "401") {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text(Constants
.sessionExpiredPleaseLoginAgain)));
Navigator.pushReplacementNamed(
context, AppRouter.loginPath);
}
}
},
builder: (context, state) {
return BlocBuilder<SupportBloc, SupportState>(
builder: (context, state) {
if (state is SupportLoaded) {
return cardView(state.faqs);
} else if (state is SupportLoading) {
return const Expanded(
child: Center(
child: CircularProgressIndicator(),
),
);
} else if (state is SupportInitial) {
return const SizedBox.shrink();
} else {
return Container();
}
});
},
),
],
),
),
)),
);
}
Widget cardView(results) {
return Expanded(
child: Container(
margin: const EdgeInsets.only(bottom: 20),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
color: CommonFunctions.getThemeColor(context,
lightColor: ColorCode.colorWhite,
darkColor: ColorCode.textFieldDark)),
child: cardContainer(results),
),
);
}
Widget cardContainer(results) {
return ListView.builder(
controller: _scrollController,
key: Key('${selected.toString()}'),//here becuase of key it is causing rebuild
itemCount: results.length,
itemBuilder: (context, i) {
return Column(
mainAxisSize: MainAxisSize.min,
children: [
lists(i, results),
if (i != results.length - 1)
Padding(
padding: EdgeInsets.symmetric(horizontal: 15.0),
child: Divider(
color: CommonFunctions.getThemeColor(context,
lightColor: Color.fromRGBO(0, 0, 0, 0.4),
darkColor: Color.fromRGBO(229, 229, 229, 0.4))),
),
],
);
},
);
}
Text title() {
return const Text(
Constants.support,
style: TextStyle(
fontFamily: 'PublicSans-Medium',
fontWeight: FontWeight.w700,
fontSize: 20),
);
}
Theme lists(int i, results) {
return Theme(
data: ThemeData().copyWith(
dividerColor: Colors.transparent,
cardColor: Colors.white,
shadowColor: Colors.transparent,
primaryColor: ColorCode.secondaryColor),
child: ExpansionTile(
iconColor: ColorCode.secondaryColor,
collapsedIconColor: ColorCode.secondaryColor,
key: Key(i.toString()),
initiallyExpanded: i == selected,
title: Text(
results[i].question as String,
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w500,
fontFamily: 'PublicSans-Light',
color: CommonFunctions.getThemeColor(context,
lightColor: ColorCode.black,
darkColor: ColorCode.colorWhite)),
),
children: [
Padding(
padding: const EdgeInsets.only(left: 15.0, bottom: 15, right: 15),
child: Align(
alignment: Alignment.centerLeft,
child: Text(
results[i].answer as String,
style: const TextStyle(
color: ColorCode.grey_600,
fontSize: 14,
fontFamily: 'PublicSans-Thin',
height: 1.5),
textAlign: TextAlign.start,
),
),
),
],
tilePadding: const EdgeInsets.symmetric(vertical: 5, horizontal: 15),
onExpansionChanged: ((newState) {
if (newState) {
setState(() {
selected = i;
});
} else {
setState(() {
selected = -1;
});
}
})),
);
}
}
As soon I expand the Expansion tile the other expansion tile should collapse. It is happening because of the key given to ListView.builder. But as soon as selected value changes it rebuilds the page and the ListView jumps to top as soon as I expand the tiles in the bottom . I want only one Expansion tile to be open at a time and the listView should not jump to top. Here as I scroll I'm fetching the data from server and adding it end of the list. If I remove 'key' in ListView.builder i can open any number of expansion tiles but that is not the desirable output..How to solve this ?
I want to update my Collection with an NumberPicker in a Alert Dialog. I do not get any errors in code or from the emulator. When i press the button to update the code the terminal do not give any errors. Everything looks fine but for some reason i do not work. When you need more Information just leave a comment with what you excactly need. :)
import 'package:flutter/material.dart';
import 'package:numberpicker/numberpicker.dart';
import 'package:percent_indicator/circular_percent_indicator.dart';
import 'package:testapp/services/Services.dart';
import 'models/Goals.dart';
class Statistics extends StatefulWidget {
#override
_StatisticsState createState() => _StatisticsState();
}
class _StatisticsState extends State<Statistics> {
int _currentFirstValue = 1;
int totalFirst;
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
height: 260,
child: StreamBuilder(
stream: FirestoreService().getGoals(),
builder: (context, AsyncSnapshot<List<Goal>> snapshot) {
if (snapshot.hasError || !snapshot.hasData) {
return Center(child: CircularProgressIndicator(
backgroundColor: Color(0XFF1954A1),
));
}
return ListView.builder(
scrollDirection: Axis.horizontal,
itemCount: 1,
itemBuilder: (BuildContext context, int index) {
// ignore: missing_return
Goal goal = snapshot.data[index];
return Row(
children: <Widget>[
Container(
padding: EdgeInsets.all(10),
margin: EdgeInsets.symmetric(horizontal: 20, vertical: 20),
height: 230,
width: 350,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.all(Radius.circular(10.0)),
boxShadow: [
BoxShadow(
color: Colors.grey[300],
offset: const Offset(0.5, 1),
blurRadius: 4.0,
spreadRadius: 0.1,
),
]),
child: Column(
children: <Widget>[
Row(
children: <Widget>[
Text('WeekGoals', style: TextStyle(
fontSize: 25,
fontWeight: FontWeight.w500,
),),
SizedBox(width: 100),
SizedBox(
height: 20,
width: 87,
child: FlatButton(
child: Text('edit', style: TextStyle(
fontSize: 17,
color: Colors.yellow[700]
),),
onPressed: () {
return showDialog(
context: context,
barrierDismissible: true,
builder: (context) => AlertDialog(
content: Column(
children: <Widget>[
Text('weekly goals'),
NumberPicker.integer(
initialValue: _currentFirstValue,
minValue: 1,
maxValue: 100,
onChanged: (newGoal) => setState(() => {
_currentFirstValue = newGoal,
totalFirst = _currentFirstValue,
})
),
Row(
children: <Widget>[
RaisedButton(
child: Text('edit goals'),
onPressed: () async {
Goal goal = Goal(
weekActivityGoal: totalFirst,
);
await FirestoreService().updateGoal(goal);
Navigator.pop(context, false);
},
),
FlatButton(
child: Text('Close'),
onPressed: () {
Navigator.pop(context, false);
},
)
],
)
],
),
)
);
},
),
)
],
),
SizedBox(height: 10),
Row(
children: <Widget>[
Container(
padding: EdgeInsets.symmetric(horizontal: 17.5),
child: CircularPercentIndicator(
header: Text('activitys', style: TextStyle(
fontSize: 17,
),),
radius: 130,
progressColor: Colors.red,
lineWidth: 8,
backgroundColor: Colors.grey[200],
percent: goal.weekActivity*100/goal.weekActivityGoal,
center: Text('${goal.weekActivity}/${goal.weekActivityGoal}'),
),
),
],
),
],
),
),
],
);
});
}),
),
);
}
}
Here this has been helping a lot of people try i out might help you too.
StreamBuilder(
stream: Firestore.instance.collection('Hearings').snapshots(),
builder: (context, snapshot) {
if (snapshot.hasError) return Text('Error: ${snapshot.error}');
switch (snapshot.connectionState) {
case ConnectionState.none:
return Text('Select lot');
case ConnectionState.waiting:
return Text('Awaiting bids...');
case ConnectionState.active:
{
print('active');
return Text('${snapshot.data}');
}
case ConnectionState.done:
{
print('Done');
return _buildList(context, snapshot.data);
}
}
return null;
}),
));
}
Widget _buildList(BuildContext context, List<DocumentSnapshot> snapshot) {
return ListView(
padding: const EdgeInsets.only(top: 20.0),
children: snapshot.map((data) => _buildListItem(context, data)).toList(),
);
}
Widget _buildListItem(BuildContext context, DocumentSnapshot data) {
final record = Record.fromSnapshot(data);
return Padding(
key: ValueKey(record.name),
padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0),
child: Container(
decoration: BoxDecoration(
border: Border.all(color: Colors.grey),
borderRadius: BorderRadius.circular(5.0),
),
child: ListTile(
title: Text(record.name),
trailing: Text(record.votes.toString()),
onTap: () => Firestore.instance.runTransaction((transaction) async {
final freshSnapshot = await transaction.get(record.reference);
final fresh = Record.fromSnapshot(freshSnapshot);
await transaction
.update(record.reference, {'votes': fresh.votes + 1});
}),
),
),
);
}
}
class Record {
final String name;
final int votes;
final DocumentReference reference;
Record.fromMap(Map<String, dynamic> map, {this.reference})
: assert(map['name'] != null),
assert(map['votes'] != null),
name = map['name'],
votes = map['votes'];
Record.fromSnapshot(DocumentSnapshot snapshot)
: this.fromMap(snapshot.data, reference: snapshot.reference);
#override
String toString() => "Record<$name:$votes>";
}
This is where the link this info came from.