Flutter CustomScrollView and Scoped model problems - dart

I'm facing some issues quite the time lately. I'm using the scoped_model package for my application. But i'm getting the problem that when i'm searching for stuff or i turn my screen to landscape modus it builds twice.
When this happens i'm getting duplicate json because it is rebuilding twice.
I'm getting another issue when i have searched and i go back my custom scroll view is not working anymore. I can't scroll down anymore.
I have writtin the following files:
main.dart:
class GetRelations extends StatefulWidget {
#override
RelationsPage createState() => RelationsPage();
}
class RelationsPage extends State<GetRelations> {
final RelationScopedModel relationScopedModel = RelationScopedModel();
#override
Widget build(BuildContext context) {
relationScopedModel.initializeValues();
return Scaffold(
body: ScopedModel<RelationScopedModel>(
model: relationScopedModel,
child: Container(
child: SearchScreen(),
),
),
);
}
}
search_screen.dart:
class SearchScreen extends StatefulWidget {
#override
SearchScreenState createState() {
return new SearchScreenState();
}
}
class SearchScreenState extends State<SearchScreen> {
final RelationScopedModel relationScopedModel = RelationScopedModel();
ScrollController controller;
int page = 0;
bool atEnd = false;
#override
void initState() {
super.initState();
controller = new ScrollController()..addListener(_scrollListener);
}
#override
void dispose() {
super.dispose();
controller.dispose();
}
void _scrollListener() {
var props = RelationScopedModel.of(context);
/// When reload we check if skip == 0, if skip is 0 then page has to become 0 too.
if (props.skip == 0) {
page = 0;
}
/// Checking if user is at the end of the screen, then let's receive some new content.
if (controller.position.pixels == controller.position.maxScrollExtent) {
/// If it is at the end, we have to set atEnd to true.
if (props.atTheEnd == true) {
atEnd = props.atTheEnd;
return;
}
/// If it has no more pages, return.
if (props.hasMorePages == false) {
return;
}
/// If it is has more stuff, load it in!
if (!props.isLoadingMore && props.hasMorePages) {
page++;
props.getRelations(props.search, page);
}
}
}
#override
Widget build(BuildContext context) {
/// Go back to last page by using WillPopScope.
return WillPopScope(
onWillPop: () {
Navigator.pop(context);
},
child: new Scaffold(
drawer: new DrawerOnly(),
/// We are using Scoped Model to load the json in the sliver list.
body: ScopedModelDescendant<RelationScopedModel>(
builder: (context, child, model) {
return CustomScrollView(
controller: controller,
slivers: <Widget>[
SliverAppBar(
title: SearchWidget(
performSearch: model.getRelations,
),
floating: true,
pinned: true,
),
model.isLoading && model.atTheEnd
? SliverFillRemaining(
child: Center(
child: CircularProgressIndicator(),
),
)
: model.getRelationCount() < 1
? SliverFillRemaining(
child: Center(
child: Text(
model.statusText,
style: Theme.of(context).textTheme.headline,
),
),
)
: SliverList(
delegate: SliverChildBuilderDelegate(
(context, index) {
if (index == model.getRelationCount() + 1) {
if (model.hasMorePages == true) {
return Padding(
padding: const EdgeInsets.symmetric(
vertical: 16.0),
child: Center(
child: CircularProgressIndicator()),
);
}
return Container(width: 0, height: 0);
} else if (index == 0) {
return Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
border: Border(
bottom: BorderSide(
color: Colors.grey[300]))),
child: Text(
"Relaties",
style: Theme.of(context)
.textTheme
.body2
.copyWith(color: Colors.white),
),
);
} else {
return Container(
child: Column(
children: <Widget>[
InkWell(
child: RelationItem(
model.relation[index - 1]),
onTap: () {
var id =
model.relation[index - 1].id;
Navigator.of(context).push(
new MaterialPageRoute(
builder: (BuildContext
context) =>
new DetailScreen(id)));
},
),
],
),
);
}
},
childCount: model.getRelationCount() + 2,
),
)
],
);
},
),
),
);
}
}
search.dart (this is my searchwidget):
class SearchWidget extends StatelessWidget { final performSearch;
const SearchWidget({Key key, #required this.performSearch}) : super(key: key);
#override Widget build(BuildContext context) {
print('wat gebeurt er');
return Card(
elevation: 3.0,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(2.0)),
),
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 15.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
GestureDetector(
child: Icon(
Icons.search,
color: Colors.white,
),
onTap: () {},
),
SizedBox(
width: 10.0,
),
Expanded(
child: TextField(
decoration: InputDecoration(
hintStyle: TextStyle(fontSize: 14.0, color: Colors.white),
border: InputBorder.none,
hintText: "Zoeken..."),
onChanged: (String search) {
if (search.length > 2) {
performSearch(search);
}
if (search.length == 0) {
performSearch(search);
}
},
),
),
InkWell(
onTap: () {
Navigator.of(context).push(
MaterialPageRoute(builder: (context) => SettingsScreen()));
},
child: Icon(
FontAwesomeIcons.slidersH,
color: Colors.white,
),
),
],
),
),
); } }
relation_scoped_model.dart:
class RelationScopedModel extends Model {
List<GetRelation> _relation = [];
List<GetContacts> _contacts = [];
List<GetLocations> _locations = [];
bool _isLoading = false;
String _statusText = "Start met zoeken...";
bool _hasMorePages = true;
int _skip = 0;
int skipMore = 0;
String _search;
bool _isLoadingMore = false;
bool atTheEnd = false;
List<Map<String, String>> _listingTypeList = [
{"name": "Buy", "value": "buy"},
{"name": "Rent", "value": "rent"},
];
String _listingType;
List<Map<String, String>> _sortList = [
{"name": "Relevancy", "value": "relevancy"},
{"name": "Bedroom (Ascending)", "value": "bedroom_lowhigh"},
{"name": "Bedroom (Descending)", "value": "bedroom_highlow"},
{"name": "Price (Ascending)", "value": "price_lowhigh"},
{"name": "Price (Descending)", "value": "price_highlow"},
{"name": "Newest", "value": "newest"},
{"name": "Oldest", "value": "oldest"},
{"name": "Random", "value": "random"},
{"name": "Distance", "value": "distance"}
];
String _sort;
List<GetRelation> get relation => _relation;
List<GetContacts> get contacts => _contacts;
List<GetLocations> get locations => _locations;
bool get isLoading => _isLoading;
String get statusText => _statusText;
bool get hasMorePages => _hasMorePages;
String get search => _search;
int get skip => _skip;
bool get isLoadingMore => _isLoadingMore;
List<Map<String, String>> get listingTypeList => _listingTypeList;
String get listingType => _listingType;
List<Map<String, String>> get sortList => _sortList;
String get sort => _sort;
int getRelationCount() => _relation.length;
void initializeValues() async {
_relation.clear();
SharedPreferences prefs = await SharedPreferences.getInstance();
_listingType = prefs.getString('listingType') ?? 'rent';
_sort = prefs.getString('sort') ?? 'relevancy';
getRelations(search);
}
var _skiptotal = 10;
var lastSearch;
Future<dynamic> _getData(String search, [int page = 0]) async {
/// When page is 0 we don't have to skip content, if page is 1 or higher it will have to skip content
if (page != 0) {
var skipPage = page * _skiptotal;
_skip = skipPage;
}
/// If page == 0, we have to set page and _skip to 0.
else {
_skip = 0;
page = 0;
}
/// When nothing is filled in the input search, we have to set search to single quotes because otherwise it will search on null.
if (search == null) {
search = '';
}
if (lastSearch != search) {
_relation.clear();
lastSearch = '';
lastSearch = search;
page + 1;
}
String _credentials;
SharedPreferences pref = await SharedPreferences.getInstance();
_credentials = (pref.getString("credentials") ?? "Empty");
var res = await http.get(
Uri.encodeFull("$cf_api_RelationsUrl" +
"?$cf_api_SkipParameter=$_skip&$cf_api_SearchParameter=$search&$cf_api_LimitParameter=10"),
headers: {
"content-type": "application/json",
"accept": "application/json",
'cookie': '$_credentials'
});
var decodedJson = json.decode(res.body, reviver: (k, v) {
if (k == "status" && v == false) {
_hasMorePages = false;
return v;
} else {
return v;
}
});
if (_hasMorePages == false) {
return decodedJson;
} else {
List list = List();
list = json.decode(res.body) as List;
if (list.length < 10) {
atTheEnd = true;
_hasMorePages = false;
return decodedJson;
}
}
return decodedJson;
}
Future getRelations(String search, [int page = 0]) async {
if (page == 0) {
page = 0;
_isLoading = true;
_relation.clear();
} else {
_isLoadingMore = true;
}
_search = search;
var responseData = await _getData(search, page);
notifyListeners();
var result = responseData
.map(
(data) => serializers.deserializeWith(GetRelation.serializer, data))
.toList();
result.forEach((relations) {
_relation.add(relations);
});
if (result.isEmpty) {
_statusText = "Nothing Found";
}
if (page == 0) {
_isLoading = false;
} else {
_isLoadingMore = false;
}
if (atTheEnd == true) {
return true;
}
// notifyListeners();
}
void setListingType(String value) async {
_listingType = value;
getRelations(search);
notifyListeners();
SharedPreferences prefs = await SharedPreferences.getInstance();
prefs.setString('listingType', _listingType);
}
void setSort(String value) async {
_sort = value;
getRelations(search);
notifyListeners();
SharedPreferences prefs = await SharedPreferences.getInstance();
prefs.setString('sort', _sort);
}
/// Wraps [ScopedModel.of] for this [Model].
static RelationScopedModel of(BuildContext context) =>
ScopedModel.of<RelationScopedModel>(context);
}
I'm guessing that the problem is that my scopedmodeldescendant isn't in the init state. But i'm not sure and i hope somebody knows what the problem is with this code.
Thanks in advance
Greetings,
Jente
UPDATE:
I got my customscrollview is working when i set atTheEnd to false and hasMorePages to true in my relation_scoped_model at initializeValues().
The only problem i'm still facing is that i'm getting duplicates, which happens because my screen is rebuilding twice.

When using scoped model, you should wrap the widgets that should respond to changes one by one with scoped model descendants. This way only those widgets can rebuild rather than the whole page rebuilding.

Related

How can I set the state in a widget that has been mounted in Flutter?

I' m new in Flutter. I made an app that has a catalogue with categories, subcategories and items (as you can see in photo). My widget tree is:
Catalogue(cart):{[Category-List, Subcategory-List, Item-List:{ItemRow(ListTile)}]}.
I'm facing the following problem: I have items in my cart and their quantity is shown on the catalogue (item rows). When I delete an item from the cart, or clear all items, I can't set the controller of the text field of the item Row to zero cause that widget (current item row) has been mounted. I use Scoped model to add, delete or update items in the cart. So, my problem is just visual. When I click on another category and then go to the previous, the controller has been set to zero correctly (cause the item rows recreated again with initstate()).
Is there any solution to my problem? Thanks!
App Image:
Catalogue.dart Code:
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter_slidable/flutter_slidable.dart';
import '../widgets/categories/categories_manager.dart';
import '../widgets/subcats/subcat_manager.dart';
import '../widgets/items/items_list.dart';
import '../scoped-models/main.dart';
import '../models/item.dart';
class CataloguePage extends StatefulWidget {
final String _langSelected;
CataloguePage(this._langSelected, this.model);
final MainModel model;
#override
State<StatefulWidget> createState() {
return _CataloguePageState();
}
}
class _CataloguePageState extends State<CataloguePage> {
Widget currentPage;
SubcatManager subcatPage;
bool _loadingProgress;
List<Item> _listCart;
final SlidableController slidableController = SlidableController();
#override
void initState() {
_listCart = widget.model.itemsInCart;
_loadingProgress = true;
widget.model
.fetchCategories(widget.model.serverAddress, widget.model.serverPort)
.then((bool success) {
if (success) {
setState(() {
widget.model
.fetchSubcats(widget.model.serverAddress, widget.model.serverPort,
widget.model.categories[0].catid)
.then((bool success2) {
if (success2) {
setState(() {
widget.model
.fetchItems(
widget.model.serverAddress,
widget.model.serverPort,
widget.model.categories[0].catid,
widget.model.subcats[0].subcatid)
.then((bool success3) {
if (success3) {
_loadingProgress = false;
}
});
});
}
});
});
} else {
showDialog(
barrierDismissible: true,
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text('An error has occured.'),
content: Text('Connection with Server failed!'),
actions: <Widget>[
FlatButton(
onPressed: () {
Navigator.popUntil(
context, (_) => !Navigator.canPop(context));
Navigator.pushReplacementNamed(context, '/');
},
child: Text('OK'),
)
],
);
});
}
});
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Theme.of(context).primaryColor,
title: Text('Catalogue'),
actions: <Widget>[
Stack(
children: <Widget>[
IconButton(
icon: Icon(
Icons.shopping_cart,
size: 30.0,
),
onPressed: () {
showModalBottomSheet(
context: context,
builder: (BuildContext contex) {
return _buildCartList(_listCart);
},
);
},
),
widget.model.itemsInCart.length > 0
? CircleAvatar(
radius: 10.0,
child: Text(widget.model.itemsInCart.length.toString()),
)
: Container()
],
)
],
),
body: _buildBody(),
);
}
Widget _buildBody() {
if (_loadingProgress) {
return Container(
color: Theme.of(context).backgroundColor,
child: Center(
child: Theme.of(context).platform == TargetPlatform.iOS
? CupertinoActivityIndicator(
radius: 20.0,
)
: CircularProgressIndicator(
strokeWidth: 3.0,
),
),
);
} else {
return Container(
padding: EdgeInsets.all(20),
color: Theme.of(context).backgroundColor,
child: Row(
children: <Widget>[
Flexible(
flex: 3,
child: Column(
children: [
Flexible(
child: CategoriesManager(widget.model),
),
],
),
),
widget.model.subcats[0].subcatid == 0
? Container()
: VerticalDivider(
color: widget.model.themeBrightness == 1
? Colors.white
: Colors.black,
),
widget.model.subcats[0].subcatid == 0
? Container()
: Flexible(
flex: 3,
child: Column(
children: [
Flexible(
child: SubcatManager(widget.model),
),
],
),
),
VerticalDivider(
color: widget.model.themeBrightness == 1
? Colors.white
: Colors.black,
),
Flexible(
flex: 4,
child: Column(
children: [
Text('Items'),
SizedBox(
height: 20.0,
),
Flexible(
child: ItemList(widget.model),
),
],
),
),
],
),
);
}
}
Widget _buildCartList(List<Item> listCart) {
Widget itemCartCards;
if (listCart.length > 0) {
itemCartCards = Padding(
padding: EdgeInsets.all(20.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Text('Selected Items'),
Text('Total Quantity: ' +
widget.model.cartTotalItems.toString()),
],
),
SizedBox(
height: 10.0,
),
Expanded(
child: ListView.separated(
separatorBuilder: (contex, index) => Divider(
color: widget.model.themeBrightness == 1
? Colors.white
: Colors.black,
),
itemBuilder: (BuildContext context, index) {
return Slidable(
key: Key(listCart[index].itemid),
controller: slidableController,
delegate: SlidableDrawerDelegate(),
actionExtentRatio: 0.25,
secondaryActions: <Widget>[
IconSlideAction(
icon: Icons.delete,
caption: 'Delete',
color: Colors.red,
onTap: () {
widget.model
.deleteItemFromCart(listCart[index].itemid);
},
)
],
child: ListTile(
title: Text(
listCart[index].itemperi,
),
trailing: Row(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
widget.model.showListItemsPrices
? Text(listCart[index].itemCount.toString() +
' x ' +
listCart[index].itemprice.toString() +
' €')
: Text(listCart[index].itemCount.toString()),
],
),
),
);
},
itemCount: listCart.length,
),
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
RaisedButton(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10)),
child: Text('Clear Order'),
color: Colors.red,
onPressed: () {
return showDialog<void>(
context: context,
barrierDismissible: false,
builder: (BuildContext context) {
return AlertDialog(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10.0),
),
title: Text('Warning!'),
content:
Text('Are you sure you want to empty your cart?'),
actions: <Widget>[
FlatButton(
child: Text('Yes'),
onPressed: () {
Navigator.of(context).pop();
Navigator.of(context).pop();
widget.model.deleteAllCartItems();
}),
FlatButton(
child: Text('No'),
onPressed: () {
Navigator.of(context).pop();
},
),
],
);
},
);
},
),
SizedBox(
width: 20.0,
),
RaisedButton(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10)),
child: Text('Confirm Order'),
color: Colors.green,
onPressed: () {
_buildJsonOrder();
},
),
],
),
],
),
);
} else {
itemCartCards = Container(
child: Center(
child: Text('Your cart is empty.'),
),
);
}
return itemCartCards;
}
void _buildJsonOrder() {
final List<dynamic> _listItems = [];
for (Item item in widget.model.itemsInCart) {
final Map<String, dynamic> itemData = {
'hallid': [widget.model.hallNumber],
'tableid': [widget.model.tableNumber],
'itemid': ['${item.itemid}'],
'itemperi': ['${item.itemperi}'],
'kind': [0],
'catid': [item.itemCatId],
'subcatid': [item.itemSubcatId],
'quantity': [item.itemCount],
'price': [item.itemprice]
};
_listItems.add(itemData);
}
final Map<String, dynamic> orderData = {
'hallid': [widget.model.hallNumber],
'tableid': [widget.model.tableNumber],
'typeofpos': ['4'],
'posid': [600],
'userid': [widget.model.currentUserId],
'items': _listItems
};
widget.model
.sendOrder(
widget.model.serverAddress, widget.model.serverPort, orderData)
.then((bool success) {
if (success) {
showDialog(
barrierDismissible: false,
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text('Success.'),
content: Text('Your order has been placed successfully!'),
actions: <Widget>[
FlatButton(
onPressed: () {
widget.model.deleteAllCartItems();
Navigator.pop(context);
Navigator.of(context).pop();
},
child: Text('OK'),
)
],
);
});
} else {
showDialog(
barrierDismissible: false,
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text('An error has occured.'),
content: Text('Something went wrong with your order.'),
actions: <Widget>[
FlatButton(
onPressed: () {
Navigator.pop(context);
},
child: Text('OK'),
)
],
);
});
}
});
}
}
ItemList.dart Code:
import 'package:flutter/material.dart';
import 'package:scoped_model/scoped_model.dart';
import '../../models/item.dart';
import '../../scoped-models/main.dart';
import './item_row.dart';
class ItemList extends StatefulWidget {
final MainModel model;
ItemList(this.model);
#override
State<StatefulWidget> createState() {
return _ItemListState();
}
}
class _ItemListState extends State<ItemList> {
#override
void initState() {
super.initState();
}
Widget _buildItemList(List<Item> items) {
Widget itemCards;
if (items.length > 0) {
itemCards = ListView.separated(
separatorBuilder: (contex, index) => Divider(
color: widget.model.themeBrightness == 1
? Colors.white
: Colors.black,
),
itemBuilder: (BuildContext context, index) {
return ItemRow(widget.model, items[index]);
},
itemCount: items.length,
);
} else {
itemCards = Container();
}
return itemCards;
}
#override
Widget build(BuildContext context) {
return Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Expanded(
child: ScopedModelDescendant<MainModel>(
builder: (BuildContext context, Widget child, MainModel model) {
return _buildItemList(model.items);
},
),
),
],
);
}
}
ItemRow.dart Code:
import 'package:flutter/material.dart';
import 'package:scoped_model/scoped_model.dart';
import '../../models/item.dart';
import '../../scoped-models/main.dart';
class ItemRow extends StatefulWidget {
final MainModel model;
final Item item;
ItemRow(this.model, this.item);
#override
State<StatefulWidget> createState() {
return _ItemRowState();
}
}
class _ItemRowState extends State<ItemRow> {
int _itemCount;
TextEditingController _itemCountController;
#override
void initState() {
setState(() {
_itemCount = widget.item.itemCount;
_itemCountController = TextEditingController(text: _itemCount.toString());
});
super.initState();
}
#override
void didUpdateWidget(ItemRow oldWidget) {
if (oldWidget.item.itemid != widget.item.itemid) {
setState(() {
_itemCount = widget.item.itemCount;
_itemCountController =
TextEditingController(text: _itemCount.toString());
});
}
super.didUpdateWidget(oldWidget);
}
#override
Widget build(BuildContext context) {
return ScopedModelDescendant<MainModel>(
builder: (BuildContext context, Widget child, MainModel model) {
return _buildItem(widget.item);
},
);
}
Widget _buildItem(Item item) {
return ListTile(
leading: CircleAvatar(
backgroundImage: item.itemimage == ''
? AssetImage('assets/noimage.png')
: NetworkImage(item.itemimage)),
title: Text(
item.itemperi,
),
trailing: Row(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
widget.model.showListItemsPrices
? Text(
item.itemprice.toString() + ' €',
)
: Container(),
widget.model.showListItemsCart ? VerticalDivider() : Container(),
widget.model.showListItemsCart
? _buildListItemCart(item)
: Container()
],
),
onTap: () {
if (widget.model.clickItems) {
Navigator.pushNamed(context, '/item/' + item.itemid);
}
},
);
}
Widget _buildListItemCart(Item item) {
return Container(
child: Row(
children: <Widget>[
GestureDetector(
onLongPress: () {
if (_itemCount != 0) {
setState(() {
_itemCount = 0;
_itemCountController =
TextEditingController(text: _itemCount.toString());
widget.model.deleteItemFromCart(item.itemid);
});
}
},
child: IconButton(
icon: Icon(Icons.remove),
onPressed: () {
if (_itemCount != 0) {
setState(() {
_itemCount--;
_itemCountController =
TextEditingController(text: _itemCount.toString());
if (_itemCount == 0) {
widget.model.deleteItemFromCart(item.itemid);
} else {
widget.model.updateItemCart(item.itemid, _itemCount);
}
});
}
},
),
),
Container(
width: 30.0,
child: TextField(
decoration: InputDecoration(border: InputBorder.none),
textAlign: TextAlign.center,
controller: _itemCountController,
keyboardType: TextInputType.numberWithOptions(),
onTap: () {
_itemCountController.selection = TextSelection(
baseOffset: 0,
extentOffset: _itemCountController.text.length);
},
onSubmitted: (value) {
if (value == 0.toString()) {
widget.model.deleteItemFromCart(item.itemid);
}
if (value != 0.toString() && _itemCount == 0) {
widget.model.addItemToCart(
item.itemid,
item.itemperi,
item.itemprice,
int.parse(value),
int.parse(widget.model.selectedCatid),
int.parse(widget.model.selectedSubcatId));
}
if (value != 0.toString() && _itemCount != 0) {
widget.model.updateItemCart(item.itemid, int.parse(value));
}
_itemCount = int.parse(value);
TextEditingController(text: _itemCount.toString());
},
),
),
IconButton(
icon: Icon(Icons.add),
onPressed: () {
setState(
() {
_itemCount++;
_itemCountController =
TextEditingController(text: _itemCount.toString());
if (_itemCount == 1) {
widget.model.addItemToCart(
item.itemid,
item.itemperi,
item.itemprice,
_itemCount,
int.parse(widget.model.selectedCatid),
int.parse(widget.model.selectedSubcatId));
} else {
widget.model.updateItemCart(item.itemid, _itemCount);
}
},
);
},
),
],
),
);
}
}
ScopedModelItems.dart Code:
import 'package:scoped_model/scoped_model.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';
import '../models/item.dart';
import '../scoped-models/main.dart';
mixin ItemsModel on Model {
MainModel model;
List<Item> _items = [];
List<Item> itemsEmpty = [];
int _cartTotalItems = 0;
List<Item> _itemsCartEmpty = [];
Item _itemDetails;
Item get itemDetails {
return _itemDetails;
}
List<Item> get items {
return List.of(_items);
}
List<Item> get itemsInCart {
return _itemsCartEmpty;
}
int get cartTotalItems {
if (_cartTotalItems == null) {
_cartTotalItems = 0;
}
return _cartTotalItems;
}
void initState() {
_items.forEach(_addItem);
}
void _addItem(Item item) {
itemsEmpty.add(item);
}
void addItemToCart(String itemid, String itemperi, num itemprice,
int itemQuantity, int itemCatid, int itemSubcatid) {
Item item = Item(
itemid: itemid,
itemperi: itemperi,
itemprice: itemprice,
itemCount: itemQuantity,
itemCatId: itemCatid,
itemSubcatId: itemSubcatid);
_itemsCartEmpty.add(item);
_cartTotalItems += itemQuantity;
notifyListeners();
}
void updateItemCart(String itemid, int itemQuantity) {
_itemsCartEmpty.forEach((item) {
if (item.itemid == itemid) {
_cartTotalItems -= item.itemCount;
item.itemCount = itemQuantity;
_cartTotalItems += itemQuantity;
}
});
notifyListeners();
}
void deleteItemFromCart(String itemid) {
_itemsCartEmpty.forEach((item) {
if (item.itemid == itemid) {
_cartTotalItems -= item.itemCount;
}
});
_itemsCartEmpty.removeWhere((item) => item.itemid == itemid);
notifyListeners();
}
void deleteAllCartItems() {
_itemsCartEmpty.removeRange(0, _itemsCartEmpty.length);
_cartTotalItems = 0;
notifyListeners();
}
Future<bool> fetchItems(String serverAddress, String serverPort,
dynamic catid, dynamic subcatid) {
return http.get(
'http://$serverAddress:$serverPort/cats/$catid/subcats/$subcatid/items/GR',
headers: {'Accept': 'application/json'}).then((http.Response response) {
if (response.statusCode == 200 || response.statusCode == 201) {
final List<Item> fetchedItemList = [];
final List<dynamic> itemListData = json.decode(response.body);
itemListData.forEach((dynamic itemData) {
String imageData = '', periData = '';
if (itemData['item_image_path'] != '') {
imageData = 'http://$serverAddress:$serverPort/photos/' +
itemData['item_image_path'];
}
if (itemData['item_webperi'] == '') {
periData = itemData['item_peri'];
} else {
periData = itemData['item_webperi'];
}
final Item item = Item(
itemid: itemData['item_id'],
itemperi: periData,
itemimage: imageData,
itemprice: itemData['item_price'],
itemCount: 0);
if (_itemsCartEmpty.isNotEmpty) {
for (Item itemCart in _itemsCartEmpty) {
if (itemCart.itemid == item.itemid) {
item.itemCount = itemCart.itemCount;
}
}
}
fetchedItemList.add(item);
});
_items = fetchedItemList;
notifyListeners();
return true;
} else {
return false;
}
}).catchError((error) {
print(error);
notifyListeners();
return false;
});
}
}
Since there's no code I'll just list the common errors that cause this when using scoped model:
1. Not calling notify
You have to call notifyListeners() in your scoped model to update the UI that's using your porperties.
2. Not telling model to rebuild
If you're not using a ScopedModelDescendent widget and instead you're getting your model using
appModel = ScopedModel.of<AppModel>(context);
change your code to
appModel = ScopedModel.of<AppModel>(context, rebuildOnChange: true);
If you're doing both of these then please post your code for your UI and scoped model so that it's easier to offer our help.

How to use SharedPreferences in Bloc Pattern?

I am trying to use shared preference in my app with the bloc pattern.
Following is my code
class PrefsStats {
final bool isMale;
final String name;
final int age;
PrefsStats(this.isMale, this.name, this.age);
}
class PrefsBloc {
final _changePrefernce = BehaviorSubject<PrefsStats>();
Function(PrefsStats) get changePrefs => _changePrefernce.sink.add;
Stream<PrefsStats> get prefrence => _changePrefernce.stream;
SharedPreferences sPrefs;
dispose(){
_changePrefernce?.close();
}
PrefsBloc(){
_loadSharedPreferences();
}
Future<void> _loadSharedPreferences() async {
sPrefs = await SharedPreferences.getInstance();
final namePref = sPrefs.getString("name") ?? "";
final malePref = sPrefs.getBool("male") ?? false;
final agePref = sPrefs.getInt("age") ?? 0;
_changePrefernce.add(PrefsStats(malePref,namePref,agePref));
}
}
final prefsBloc = PrefsBloc();
I just want to insert data using one button and get data using another button from SharedPreferences
class _MyAppState extends State<MyApp> {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: SafeArea(
child: Column(
children: <Widget>[
SizedBox(
height: 20,
),
RaisedButton(
onPressed: () {
prefsBloc.changePrefs(PrefsStats(true, "argo", 21));
},
child: Text("Insert Data"),
),
SizedBox(
height: 20,
),
RaisedButton(
onPressed: () {
prefsBloc.prefrence.forEach((data){
print(data.name);
});
},
child: Text("Get Data"),
),
SizedBox(
height: 20,
),
],
)),
),
);
}
#override
void dispose() {
prefsBloc?.dispose();
super.dispose();
}
}
Whenever I close my app and reopen it again and I click get data button at the start even before inserting data, I get default values. I know I am not assigning keys at the time of setting value, which is causing the confusion of how to use shared preferences with bloc. And the other problem is whenever I set data, the code inside get data button gets called even before pressing get data which I fail to understand.
There exits two places on your code that must be fixed.
First of all, in your BloC class, your stream must Listen whenever a sink is added,
.
.
.
PrefsBloc(){
_loadSharedPreferences();
_changePrefernce.stream.listen(_newFunction);
}
void _newFunction(PrefsStats stats){
if (states != null) {
if (sPrefs != null) {
sPrefs.setString("name", states.name);
sPrefs.setInt("age", states.age);
sPrefs.setBool("male", states.isMale);
sPrefs.commit();
}
}
}
Second place is in _MyAppState class, in the build function you have to wrap Scaffold with a StreamBuilder,
class _MyHomePageState extends State<MyHomePage> {
String textAge = "";
#override
Widget build(BuildContext context) {
return MaterialApp(
home: StreamBuilder(
stream: prefsBloc.prefrence,
builder: (context, AsyncSnapshot<PrefsStats> snapshot) {
return Scaffold(
body: SafeArea(
child: Column(
children: <Widget>[
Text((snapshot.data != null) ? snapshot.data.name : ""),
SizedBox(
height: 20,
),
RaisedButton(
onPressed: () {
prefsBloc.changePrefs(PrefsStats(
true,
textAge.toString(),
21,
));
},
child: Text("Insert Data"),
),
TextFormField(
initialValue: (snapshot.data != null) ? snapshot.data.name : "",
onFieldSubmitted: (value) {
textAge = value;
},
),
Text(textAge),
SizedBox(
height: 20,
),
RaisedButton(
onPressed: () {
prefsBloc.prefrence.forEach((data) {
print(data.name);
setState(() {
textAge = data.name;
});
});
},
child: Text("Get Data"),
),
SizedBox(
height: 20,
),
],
)),
);
},
));
}

How to take an array from user in flutter from text field?

Widget build(BuildContext context) {
TextField XnumField = new TextField(
keyboardType: TextInputType.numberWithOptions(),
decoration: new InputDecoration(labelText: "X array"),
onSubmitted: (String text){
for(i = 0; i < 4; i++){
X[i] = int.parse(text);
}
},
);
Hi,
If you are looking for something like this here you go.
import 'package:flutter/material.dart';
void main() {
runApp(new MaterialApp(
home: new DemoScreen(),
));
}
class DemoScreen extends StatefulWidget {
#override
_DemoScreenState createState() => new _DemoScreenState();
}
class _DemoScreenState extends State<DemoScreen> {
List<int> _myList = new List();
TextEditingController _myController = new TextEditingController();
String _result = "";
String _inputList = "";
setSum() {
int sum = 0;
for (int i = 0; i < _myList.length; i++) {
sum += _myList[i];
if (i == 0)
_inputList = "${_myList[i]}";
else
_inputList = _inputList + " + ${_myList[i]}";
}
_result = "$sum";
}
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text("Demo App"),
),
body: new Column(
children: <Widget>[
new Container(
margin: new EdgeInsets.symmetric(vertical: 10.0),
child: new Text(
_inputList,
style: new TextStyle(fontSize: 40.0),
),
),
new Container(
margin: new EdgeInsets.symmetric(vertical: 25.0),
child: new Text(
_result,
style: new TextStyle(fontSize: 70.0),
),
),
new Container(
margin: new EdgeInsets.symmetric(horizontal: 50.0),
child: new TextField(
controller: _myController,
keyboardType: TextInputType.number,
onSubmitted: (text) {
setState(() {
_myList.add(int.parse(text));
setSum();
_myController.clear();
});
},
),
)
],
),
);
}
}
Hope it helps :)
Something like ...
import 'dart:core';
List<int> BuildIntArray(String input) {
var outList = new List<int>();
final _delimiter = ',';
final _values = input.split(_delimiter);
_values.forEach((item) {
outList.add(int.parse(item));
});
return outList;
}
Which would ...
import 'package:IntegerArray/IntegerArray.dart' as IntegerArray;
main(List<String> arguments) {
final input = "1,2,3,4,5";
final intInputValues = IntegerArray.BuildIntArray(input);
print (intInputValues);
int sum = 0;
intInputValues.forEach((item) {
sum+=item;
});
print (sum);
}
... do this ...
Observatory listening on http://127.0.0.1:64499/
[1, 2, 3, 4, 5]
15
Process finished with exit code 0
I don't see a "tryParse" to filter out non-numeric values ... but with some validation/error checking you could add to this ...

screen not re-rendering after changing state

I'm just starting with Flutter, finished the first codelab and tried to add some simple functionality to it.
import 'package:flutter/material.dart';
import 'package:english_words/english_words.dart';
void main() => runApp(new MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'Startup Name Generator',
theme: new ThemeData(primaryColor: Colors.deepOrange),
home: new RandomWords(),
);
}
}
class RandomWords extends StatefulWidget {
#override
createState() => new RandomWordsState();
}
class RandomWordsState extends State<RandomWords> {
final _suggestions = <WordPair>[];
final _biggerFont = new TextStyle(fontSize: 18.0);
final _saved = new Set<WordPair>();
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text('Startup Name Generator'),
actions: <Widget>[
new IconButton(
icon: new Icon(Icons.list),
onPressed: _pushSaved,
)
],
),
body: _buildSuggestions(),
);
}
Widget _buildSuggestions() {
return new ListView.builder(
padding: const EdgeInsets.all(16.0),
itemBuilder: (context, i) {
if (i.isOdd) return new Divider();
final index = i ~/ 2;
if (index >= _suggestions.length) {
_suggestions.addAll(generateWordPairs().take(10));
}
return _buildRow(_suggestions[index]);
},
);
}
Widget _buildRow(WordPair pair) {
final alreadySaved = _saved.contains(pair);
return new ListTile(
title: new Text(
pair.asPascalCase,
style: _biggerFont,
),
trailing: new IconButton(
icon: new Icon(alreadySaved ? Icons.favorite : Icons.favorite_border),
color: alreadySaved ? Colors.red : null,
onPressed: () {
setState(() {
if (alreadySaved) {
_saved.remove(pair);
} else {
_saved.add(pair);
}
});
},
));
}
void _pushSaved() {
Navigator.of(context).push(
new MaterialPageRoute(
builder: (context) {
final tiles = _saved.map(
(pair) {
return _buildRow(pair);
// new ListTile(
// title: new Text(
// pair.asPascalCase,
// style: _biggerFont,
// ),
// );
},
);
final divided = ListTile
.divideTiles(
context: context,
tiles: tiles,
)
.toList();
return new Scaffold(
appBar: new AppBar(
title: new Text('Saved Suggestions'),
),
body: new ListView(children: divided),
);
},
),
);
}
}
In the Save suggestions screen I built the same row as in the Sugestions Screen.
In the Saved Sugstions screen when you click the heart icon the element is removed from the array of saved items but the screen is not re-rendered.
what am I doing wrong here?
thanks!
Update
Actually your app is working perfectly fine with me :/
Because you are not communicating the state change with the icon change. You are already changing state based on alreadySaved, notice how you managed setState()
setState(() {
if (alreadySaved) {
_saved.remove(pair);
} else {
_saved.add(pair);
}
});
In the previous block you are only removing or adding to your favourite list based on the boolean value of alreadySaved and you are not telling setState to change anything else. That is why the following does not produce a re-render even though alreadySaved is switching values
///These two lines do not know what is happening
icon: new Icon(alreadySaved ? Icons.favorite : Icons.favorite_border),
color: alreadySaved ? Colors.red : null,
So you can instead do the following
icon: new Icon(_whichIcon), //initialized var _whichIcon = Icons.favorite_border
color: _whichIconColor, //Initialized var _whichIconColor = Colors.transparent
And your setState would be:
setState(() {
if (alreadySaved) {
_saved.remove(pair);
_whichIcon = Icons.favorite_border ;
_whichIconColor = Colors.transparent;
} else {
_saved.add(pair);
_whichIcon = Icons.favorite ;
_whichIconColor = Colors.red;
}
});
Or simpler you can do it like this, and keep your icon logic unchanged:
bool alreadySaved = false;
...
setState(() {
if (_saved.contains(pair)) {
_saved.remove(pair);
alreadySaved = false;
} else {
_saved.add(pair);
alreadySaved = true;
}
});

flutter how to get a circular progress indicator working

UPDATE
I have changed to use a widget and put all the code for the Stateful Widget below.
There is lots of code to this, I changed to use a stack with a Widget loadingIndicator, but it still does not get called. My stack is around a future response, with the indicator at the bottom, I then use the onPress from a button click to call a method that changes state and then I call the other process to run. Still nothing happens with the indicator.
class ChatServerDivided extends StatefulWidget {
ChatServerDivided({Key key, this.title, this.mychat}) : super(key: key);
static const String routeName = "/ChatServerDivided";
final ChatServerList mychat;
final String title;
#override
_ChatServerDividedState createState() => new _ChatServerDividedState();
}
class _ChatServerDividedState extends State<ChatServerDivided> {
SharedPreferences prefs;
int oid = 0;
int pid = 0;
int authlevel = 0;
bool admin = false;
int type = 0;
String msgid = '';
List chatlist;
int listcount = 0;
bool grpmsg = true;
String sender = '';
String receiver = '';
String message = '';
String oname = '';
String pname = '';
String sendname;
String receivename;
String replyto = '';
String replyfrom = '';
String replysub = '';
final TextEditingController _newreplycontroller = new TextEditingController();
String myfcmtoken = 'NONE';
static ScrollController _scrollController;
bool messSync = false;
//Future<http.Response> _responseFuture;
Future<List<Map>> _responseFuture;
var _urlDates = '';
Future<File> _imageFile;
String myimage;
String myvideo;
File myimagefile;
File myvidfile;
Future<int> myimagelength;
String myext;
VideoPlayerController vcontroller;
bool isImage = false;
bool isVideo = false;
bool submitting = false;
//ScrollController scontroller = new ScrollController();
_getPrefs() async {
prefs = await SharedPreferences.getInstance();
if (mounted) {
setState(() {
oid = prefs.getInt('oid');
pid = prefs.getInt('pid');
authlevel = prefs.getInt('authlevel');
admin = prefs.getBool('admin');
type = 1;
msgid = widget.mychat.msgkey;
if (widget.mychat.grpid == 0) {
grpmsg = false;
} else {
grpmsg = true;
}
oname = widget.mychat.oname;
pname = widget.mychat.pname;
myfcmtoken = prefs.getString('fcmtoken');
if (authlevel == 0) {
sender = 'o';
receiver = 'p';
sendname = widget.mychat.oname;
receivename = widget.mychat.pname;
} else if (authlevel == 1) {
sender = 'p';
receiver = 'o';
sendname = widget.mychat.pname;
receivename = widget.mychat.oname;
}
//_getChats();
});
}
}
#override
void initState() {
super.initState();
//controller = new TabController(length: 4, vsync: this);
_scrollController = new ScrollController();
//_scrollController.position.maxScrollExtent;
_getPrefs();
_urlDates =
'http://$baseurl/chat/messages/getdates/${widget
.mychat.msgkey}';
_responseFuture = ChatDB.instance.getMessagesDates(widget.mychat.msgkey);
}
#override
void dispose() {
super.dispose();
if (vcontroller != null) {
vcontroller.dispose();
}
}
var jsonCodec = const JsonCodec();
var _focusnode = new FocusNode();
_getChats() async {
var _url =
'http://$baseurl/chat/messages/getdates/$msgid';
var http = createHttpClient();
var response = await http.get(_url, headers: getAuthHeader());
var chats = await jsonCodec.decode(response.body);
if (mounted) {
setState(() {
chatlist = chats.toList();
listcount = chatlist.length;
//replysub = 'Re: ' + chatlist[0]['sub'];
});
}
}
Future<Null> _onRefresh() {
Completer<Null> completer = new Completer<Null>();
Timer timer = new Timer(new Duration(seconds: 1), () {
setState(() {
_responseFuture =
ChatDB.instance.getMessagesDates(widget.mychat.msgkey);
print('RUNNING LOAD AFTER REFRESH AGAIN');
});
completer.complete();
});
return completer.future;
}
Future<String> doImageString() async {
return (await _imageFile)
.path
.substring((await _imageFile).path.length - 3);
}
#override
Widget build(BuildContext context) {
_toggleProgress() {
setState(() {
submitting = true;
});
};
Widget loadingIndicator =submitting? new Container(
color: Colors.grey[300],
width: 70.0,
height: 70.0,
child: new Padding(padding: const EdgeInsets.all(5.0),child: new Center(child: new CircularProgressIndicator())),
):new Container();
Widget mytitle;
if (grpmsg) {
mytitle = new Row(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
new Icon(Icons.people),
new Text(' '),
new Text(widget.mychat.referralname)
],
);
} else {
mytitle = new Row(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
new Icon(Icons.person),
new Text(' '),
new Text(widget.mychat.referralname)
],
);
}
var _children = <Widget>[
new Flexible(
child: new Stack(
children: <Widget>[
new FutureBuilder<List<Map>>(
future: _responseFuture,
builder:
(BuildContext context, AsyncSnapshot<List<Map>> snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.none:
return new Text('Waiting to start');
case ConnectionState.waiting:
return new Text('Loading...');
default:
if (snapshot.hasError) {
return new Text('Error: ${snapshot.error}');
} else {
return new RefreshIndicator(
child: new ListView.builder(
itemBuilder: (context, index) {
return new MyChatWidget(
datediv: snapshot.data[index]['msgdate'],
msgkey: snapshot.data[index]['msgkey'],
);
},
//itemBuilder: _itemBuilder,
controller: _scrollController,
reverse: true,
itemCount: snapshot.data.length,
),
onRefresh: _onRefresh
);
}
}
},
),
new Align(child: loadingIndicator,alignment: FractionalOffset.center,),
],
),
),
new Container(
alignment: Alignment.bottomLeft,
padding: new EdgeInsets.only(left: 10.0),
child: new FutureBuilder<File>(
future: _imageFile,
builder: (BuildContext context, AsyncSnapshot<File> snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
//return new Image.file(snapshot.data);
myimagefile = snapshot.data;
myext = path.extension(myimagefile.path);
if (myext == '.jpg') {
isImage = true;
return new Column(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
new Container(
alignment: Alignment.bottomLeft,
width: 150.0,
child: new Image.file(snapshot.data),
),
new FlatButton(
onPressed: _doClear,
child: new Text('Clear Image'))
],
);
} else {
isVideo = true;
myvidfile = new File(
snapshot.data.path.replaceAll('file://', ''));
vcontroller = new VideoPlayerController(myimagefile.path)..initialize();
return new Column(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
new Container(
alignment: Alignment.bottomLeft,
width: 150.0,
child: new vplayer.VideoCard(
controller: vcontroller,
title: widget.mychat.referralname,
subtitle: 'Video',
),
),
new FlatButton(
onPressed: _doClear,
child: new Text('Clear Video'))
],
);
}
} else {
return const Text('');
}
})
),
new Divider(
height: 5.0,
color: Colors.grey,
),
new Row(
crossAxisAlignment: CrossAxisAlignment.end,
children: <Widget>[
new Container(
alignment: Alignment.bottomLeft,
//width: 50.0,
child: new IconButton(
icon: new Icon(Icons.add_a_photo),
onPressed: _pickImage,
alignment: Alignment.bottomLeft,
),
),
new Flexible(
child: new Container(
alignment: Alignment.center,
//width: 350.0,
child: new TextField(
decoration: const InputDecoration(
hintText: 'Reply',
labelText: 'Reply:',
),
autofocus: false,
focusNode: _focusnode,
maxLines: 1,
controller: _newreplycontroller,
keyboardType: TextInputType.text,
),
),
),
new Container(
alignment: Alignment.bottomRight,
//width: 50.0,
child: new IconButton(
icon: new Icon(Icons.send),
onPressed: () {
_toggleProgress();
_sendReply();
},
alignment: Alignment.centerRight,
disabledColor: Colors.grey,
)),
],
),
];
return new Scaffold(
appBar: new AppBar(
title: mytitle,
actions: getAppBarActions(context),
),
body: new Column(
children: _children,
),
);
}
DateTime getDateDiv(int index) {
DateTime msgdate = DateTime.parse(chatlist[index]['chatdate']).toLocal();
return msgdate;
}
_doClear() {
setState(() {
_imageFile = null;
});
}
_pickImage() async {
await setState(() {
_imageFile = ImagePicker.pickImage(maxWidth: 600.0);
});
}
_sendReply() {
if (_newreplycontroller.text.isEmpty && myimagefile == null) {
/*showDialog(
context: context,
child: new AlertDialog(
content: new Text("There is no message to submit"),
actions: <Widget>[
new FlatButton(
child: const Text('OK'),
onPressed: () {
Navigator.pop(context, false);
}),
],
),
);*/
} else {
TextInputAction.done;
DateTime dateSubmit = new DateTime.now();
if (myimagefile != null) {
if (isImage) {
List<int> imageBytes = myimagefile.readAsBytesSync();
myimage = BASE64.encode(imageBytes);
myvideo = 'NONE';
}
if (isVideo) {
List<int> imageBytes = myvidfile.readAsBytesSync();
myvideo = BASE64.encode(imageBytes);
myimage = 'NONE';
}
} else {
myimage = 'NONE';
myvideo = 'NONE';
}
var mymessage = _newreplycontroller.text;
ChatServerMessage mychat = new ChatServerMessage(
widget.mychat.msgkey,
'message',
widget.mychat.refid,
widget.mychat.referralname,
replysub,
oid,
oname,
pid,
pname,
sender,
sendname,
receiver,
receivename,
mymessage,
dateSubmit.toString(),
widget.mychat.grpid.toString(),
widget.mychat.prid.toString(),
myfcmtoken,
myimage,
myvideo,
myext);
_doSendReply(mychat);
}
}
_doSendReply(mychat) async {
var json = jsonCodec.encode(mychat);
ChatConnect.instance.sendmessage(json);
_checkSync () {
messSync = ChatConnect.instance.isSynced;
if (messSync) {
if (isImage) {
Timer synctimer = new Timer(new Duration(seconds: 2), _checkSync);
} else if (isVideo) {
Timer synctimer = new Timer(new Duration(seconds: 5), _checkSync);
} else {
Timer synctimer = new Timer(new Duration(seconds: 2), _checkSync);
}
} else {
setState(() {
submitting = false;
_onRefresh();
_doClear();
print('RUNNING LOAD AFTER SEND AGAIN');
});
_newreplycontroller.text = '';
_focusnode.unfocus();
}
}
_checkSync();
}
}
I had Created a example based on your query
import 'package:flutter/material.dart';
void main() {
runApp(new MaterialApp(home: new sample()));
}
class sample extends StatefulWidget {
#override
_sampleState createState() => new _sampleState();
}
class _sampleState extends State<sample> {
bool _progressBarActive = true;
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar:new AppBar(
title: new Text("Circular progressbar demo"),
),
body: _progressBarActive == true?const CircularProgressIndicator():new Container());
}
}
I created an example based on your code snippet above. Black app with an button to toggle the submitting value using setState().
Tapping the FloatingActionButton calls setState, toggling the value of submitting. And the progress indicator is shown.
import 'package:flutter/material.dart';
void main() {
runApp(new MaterialApp(home: new SubmitPage()));
}
class SubmitPage extends StatefulWidget {
#override
_SubmitPageState createState() => new _SubmitPageState();
}
class _SubmitPageState extends State<SubmitPage> {
bool submitting = false;
void toggleSubmitState() {
setState(() {
submitting = !submitting;
});
}
#override
Widget build(BuildContext context) {
return new Scaffold(
body: new Center(
child: !submitting
? new Container(
color: Colors.grey,
)
: const Center(child: const CircularProgressIndicator()),
),
floatingActionButton: new FloatingActionButton(
child: new Icon(Icons.refresh),
onPressed: toggleSubmitState,
),
);
}
}
Why don't you use .gif image instead?
Simply place your gif in your images folder of your project and mention it in pubspec.yaml file,just like you do for images.
Image(
image: AssetImage('images/circular-progress-bar.gif'),
width: 40,
height:40
);

Resources