currently I am trying to update a text in my popupmenu, in between the add and reduce icon button, when i click on add button. Value increases but it does not update when i setstate it. I have to close the popupmenu and reopen it to see the new updated value. Any tips to improve my code would be greatly appreciated. Thank you for your help.
This is my code:
class SelectMedicalItems extends StatefulWidget {
final Function callBack;
SelectMedicalItems({this.callBack});
#override
_SelectMedicalItemsState createState() => _SelectMedicalItemsState();
}
class _SelectMedicalItemsState extends State<SelectMedicalItems>
with SingleTickerProviderStateMixin {
AnimationController _animationController;
double _scale;
int tempCount = 0;
String tempName = '';
List<CartItems> totalItem = [];
TextEditingController textController = TextEditingController();
void _onTap() {
_animationController.forward();
}
void _onTapReverse() {
_animationController.reverse();
}
void _onTapUp(TapUpDetails details) {
_animationController.reverse();
}
void _onTapDown(TapDownDetails details) {
_animationController.forward();
}
List items = List<String>();
List<String> tempMedical = medicalItems;
void filteredSearch(String query) {
items = tempMedical
.where((txt) => query.isEmpty || txt.toUpperCase().contains(query))
.toList();
setState(() {});
}
GlobalKey<ScaffoldState> _scaffoldState = GlobalKey<ScaffoldState>();
void _showBar(String newValue) async {
_scaffoldState.currentState.showSnackBar(
SnackBar(
duration: Duration(seconds: 2),
content: Text('You have selected: $newValue'),
),
);
await showDialog(
barrierDismissible: false,
context: context,
child: StatefulBuilder(builder: (context, setState) {
return AlertDialog(
elevation: 30,
backgroundColor: Color(0xFFE6F0F9),
contentPadding: EdgeInsets.all(10),
shape:
RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)),
title: Text(
'How many do you need?',
style: TextStyle(
color: Color(0xFF67696F),
),
),
content: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Expanded(
child: Padding(
padding: EdgeInsets.all(15.0),
child: TextField(
autofocus: true,
keyboardType: TextInputType.numberWithOptions(),
onSubmitted: (value) {
setState(() {
tempCount = int.parse(value);
});
},
onChanged: (value) {
setState(() {
tempCount = int.parse(value);
});
},
decoration: InputDecoration(
hintText: 'e.g 10', suffixText: 'pcs'),
),
),
),
],
),
actions: <Widget>[
FlatButton(
onPressed: () {
setState(() {
tempCount = 0;
tempName = '';
});
Navigator.pop(context);
},
child: Text(
'Cancel',
style: TextStyle(color: Colors.red),
)),
FlatButton(
onPressed: () {
setState(() {
totalItem.add((CartItems(
cartItem: tempName, itemQuantity: tempCount)));
tempName = '';
tempCount = 0;
});
Navigator.pop(context);
},
child: Text(
'Okay',
style: TextStyle(color: Colors.blue),
)),
],
);
}));
setState(() {});
}
#override
void initState() {
items = tempMedical;
_animationController = AnimationController(
vsync: this,
upperBound: 0.1,
lowerBound: 0.0,
duration: Duration(milliseconds: 100))
..addListener(() {
setState(() {});
});
super.initState();
}
#override
void dispose() {
_animationController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
_scale = 1 - _animationController.value;
return SafeArea(
child: Scaffold(
backgroundColor: Color(0xFFE6F0F9),
resizeToAvoidBottomPadding: false,
key: _scaffoldState,
body: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Padding(
padding: EdgeInsets.all(15.0),
child: Row(
children: <Widget>[
GestureDetector(
onTapUp: _onTapUp,
onTapDown: _onTapDown,
onTap: () {
_onTap();
_onTapReverse();
Future.delayed(Duration(milliseconds: 500), () {
Navigator.pop(context);
});
},
child: Transform.scale(
scale: _scale,
child: Container(
padding: EdgeInsets.all(15),
decoration: BoxDecoration(
color: Color(0xFFE6F0F9),
borderRadius: BorderRadius.circular(15),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.1),
offset: Offset(5, 5),
blurRadius: 8,
),
BoxShadow(
color: Colors.white.withOpacity(0.5),
offset: Offset(-5, -5),
blurRadius: 8,
)
]),
child: Icon(
Icons.arrow_back_ios,
color: Color(0xFF67696F),
),
),
),
),
Spacer(),
Badge(
elevation: 5,
position: BadgePosition.topRight(top: -1, right: -5),
animationDuration: Duration(seconds: 1),
toAnimate: true,
animationType: BadgeAnimationType.slide,
badgeContent: Text(
totalItem.length.toString(),
style: TextStyle(color: Colors.white),
),
child: PopupMenuButton(
color: Color(0xFFE6F0F9),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(15),
),
itemBuilder: (context) {
return totalItem
.map((item) => PopupMenuItem(
value: item.cartItem,
child: Row(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Flexible(
child: Text(
'${item.cartItem}',
style: TextStyle(
color: Colors.grey,
fontWeight: FontWeight.w500),
),
),
Spacer(),
GestureDetector(
onTap: () {
setState(() {
item.itemQuantity++;
});
},
child: Icon(
Icons.add_circle,
color: Colors.green,
),
),
Text(
item.itemQuantity.toString(),
style: TextStyle(color: Colors.black),
),
GestureDetector(
onTap: () {
setState(() {
item.itemQuantity--;
});
},
child: Icon(
Icons.remove_circle,
color: Colors.red,
),
),
],
),
))
.toList();
},
child: Container(
padding: EdgeInsets.all(15),
decoration: BoxDecoration(
color: Color(0xFFE6F0F9),
borderRadius: BorderRadius.circular(15),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.1),
offset: Offset(5, 5),
blurRadius: 8,
),
BoxShadow(
color: Colors.white.withOpacity(0.5),
offset: Offset(-5, -5),
blurRadius: 8,
)
]),
child: Icon(
Icons.shopping_cart,
color: Color(0xFF67696F),
)),
),
),
],
),
),
Padding(
padding: EdgeInsets.all(15.0),
child: TextField(
controller: textController,
decoration: InputDecoration(
hintText: 'Search',
labelText: 'Search',
prefixIcon: Icon(Icons.search),
border: OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(25.0))),
suffix: GestureDetector(
child: Icon(Icons.clear),
onTap: () {
textController.clear();
setState(() {
items = tempMedical;
});
},
)),
onChanged: (value) {
filteredSearch(value.toUpperCase());
},
),
),
Expanded(
child: ListView.builder(
itemCount: items.length,
itemBuilder: (context, index) {
return Material(
color: Color(0xFFE6F0F9),
child: InkWell(
child: ListTile(
onTap: () {
SystemSound.play(SystemSoundType.click);
_showBar('${items[index]}');
widget.callBack(items[index]);
tempName = '${items[index]}';
},
title: Text(
'${items[index]}',
style: TextStyle(color: Color(0xFF67696F)),
),
trailing: Icon(
Icons.add_circle_outline,
color: Colors.green,
size: 30,
),
),
),
);
})),
],
),
),
);
}
}
class CartItems {
final String cartItem;
int itemQuantity;
CartItems({this.cartItem, this.itemQuantity: 1});
}
You can copy-paste run full code below
You can use StatefulBuilder as child of PopupMenuItem and return Row
Code Snippet:
PopupMenuItem(
value: item.cartItem,
child: StatefulBuilder(
builder: (BuildContext context, StateSetter setState) {
return Row(
mainAxisSize: MainAxisSize.min,
Working Demo:
Full Code:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: SelectMedicalItems(),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
void _incrementCounter() {
setState(() {
_counter++;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.display1,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}
class SelectMedicalItems extends StatefulWidget {
final Function callBack;
SelectMedicalItems({this.callBack});
#override
_SelectMedicalItemsState createState() => _SelectMedicalItemsState();
}
class _SelectMedicalItemsState extends State<SelectMedicalItems>
with SingleTickerProviderStateMixin {
AnimationController _animationController;
double _scale;
int tempCount = 0;
String tempName = '';
List<CartItems> totalItem = [CartItems(cartItem: "apple", itemQuantity: 2)];
TextEditingController textController = TextEditingController();
void _onTap() {
_animationController.forward();
}
void _onTapReverse() {
_animationController.reverse();
}
void _onTapUp(TapUpDetails details) {
_animationController.reverse();
}
void _onTapDown(TapDownDetails details) {
_animationController.forward();
}
List items = List<String>();
List<String> tempMedical = ["medical"]; //medicalItems;
void filteredSearch(String query) {
items = tempMedical
.where((txt) => query.isEmpty || txt.toUpperCase().contains(query))
.toList();
setState(() {});
}
GlobalKey<ScaffoldState> _scaffoldState = GlobalKey<ScaffoldState>();
void _showBar(String newValue) async {
_scaffoldState.currentState.showSnackBar(
SnackBar(
duration: Duration(seconds: 2),
content: Text('You have selected: $newValue'),
),
);
await showDialog(
barrierDismissible: false,
context: context,
child: StatefulBuilder(builder: (context, setState) {
return AlertDialog(
elevation: 30,
backgroundColor: Color(0xFFE6F0F9),
contentPadding: EdgeInsets.all(10),
shape:
RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)),
title: Text(
'How many do you need?',
style: TextStyle(
color: Color(0xFF67696F),
),
),
content: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Expanded(
child: Padding(
padding: EdgeInsets.all(15.0),
child: TextField(
autofocus: true,
keyboardType: TextInputType.numberWithOptions(),
onSubmitted: (value) {
setState(() {
tempCount = int.parse(value);
});
},
onChanged: (value) {
setState(() {
tempCount = int.parse(value);
});
},
decoration: InputDecoration(
hintText: 'e.g 10', suffixText: 'pcs'),
),
),
),
],
),
actions: <Widget>[
FlatButton(
onPressed: () {
setState(() {
tempCount = 0;
tempName = '';
});
Navigator.pop(context);
},
child: Text(
'Cancel',
style: TextStyle(color: Colors.red),
)),
FlatButton(
onPressed: () {
setState(() {
totalItem.add((CartItems(
cartItem: tempName, itemQuantity: tempCount)));
tempName = '';
tempCount = 0;
});
Navigator.pop(context);
},
child: Text(
'Okay',
style: TextStyle(color: Colors.blue),
)),
],
);
}));
setState(() {});
}
#override
void initState() {
items = tempMedical;
_animationController = AnimationController(
vsync: this,
upperBound: 0.1,
lowerBound: 0.0,
duration: Duration(milliseconds: 100))
..addListener(() {
setState(() {});
});
super.initState();
}
#override
void dispose() {
_animationController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
_scale = 1 - _animationController.value;
return SafeArea(
child: Scaffold(
backgroundColor: Color(0xFFE6F0F9),
resizeToAvoidBottomPadding: false,
key: _scaffoldState,
body: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Padding(
padding: EdgeInsets.all(15.0),
child: Row(
children: <Widget>[
GestureDetector(
onTapUp: _onTapUp,
onTapDown: _onTapDown,
onTap: () {
_onTap();
_onTapReverse();
Future.delayed(Duration(milliseconds: 500), () {
Navigator.pop(context);
});
},
child: Transform.scale(
scale: _scale,
child: Container(
padding: EdgeInsets.all(15),
decoration: BoxDecoration(
color: Color(0xFFE6F0F9),
borderRadius: BorderRadius.circular(15),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.1),
offset: Offset(5, 5),
blurRadius: 8,
),
BoxShadow(
color: Colors.white.withOpacity(0.5),
offset: Offset(-5, -5),
blurRadius: 8,
)
]),
child: Icon(
Icons.arrow_back_ios,
color: Color(0xFF67696F),
),
),
),
),
Spacer(),
Container(
/* elevation: 5,
position: BadgePosition.topRight(top: -1, right: -5),
animationDuration: Duration(seconds: 1),
toAnimate: true,
animationType: BadgeAnimationType.slide,
badgeContent: Text(
totalItem.length.toString(),
style: TextStyle(color: Colors.white),
),*/
child: PopupMenuButton(
color: Color(0xFFE6F0F9),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(15),
),
itemBuilder: (context) {
return totalItem
.map((item) => PopupMenuItem(
value: item.cartItem,
child: StatefulBuilder(builder:
(BuildContext context, StateSetter setState) {
return Row(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Flexible(
child: Text(
'${item.cartItem}',
style: TextStyle(
color: Colors.grey,
fontWeight: FontWeight.w500),
),
),
Spacer(),
GestureDetector(
onTap: () {
setState(() {
item.itemQuantity++;
});
},
child: Icon(
Icons.add_circle,
color: Colors.green,
),
),
Text(
item.itemQuantity.toString(),
style: TextStyle(color: Colors.black),
),
GestureDetector(
onTap: () {
setState(() {
item.itemQuantity--;
});
},
child: Icon(
Icons.remove_circle,
color: Colors.red,
),
),
],
);
})))
.toList();
},
child: Container(
padding: EdgeInsets.all(15),
decoration: BoxDecoration(
color: Color(0xFFE6F0F9),
borderRadius: BorderRadius.circular(15),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.1),
offset: Offset(5, 5),
blurRadius: 8,
),
BoxShadow(
color: Colors.white.withOpacity(0.5),
offset: Offset(-5, -5),
blurRadius: 8,
)
]),
child: Icon(
Icons.shopping_cart,
color: Color(0xFF67696F),
)),
))
],
),
),
Padding(
padding: EdgeInsets.all(15.0),
child: TextField(
controller: textController,
decoration: InputDecoration(
hintText: 'Search',
labelText: 'Search',
prefixIcon: Icon(Icons.search),
border: OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(25.0))),
suffix: GestureDetector(
child: Icon(Icons.clear),
onTap: () {
textController.clear();
setState(() {
items = tempMedical;
});
},
)),
onChanged: (value) {
filteredSearch(value.toUpperCase());
},
),
),
Expanded(
child: ListView.builder(
itemCount: items.length,
itemBuilder: (context, index) {
return Material(
color: Color(0xFFE6F0F9),
child: InkWell(
child: ListTile(
onTap: () {
//SystemSound.play(SystemSoundType.click);
_showBar('${items[index]}');
widget.callBack(items[index]);
tempName = '${items[index]}';
},
title: Text(
'${items[index]}',
style: TextStyle(color: Color(0xFF67696F)),
),
trailing: Icon(
Icons.add_circle_outline,
color: Colors.green,
size: 30,
),
),
),
);
})),
],
),
),
);
}
}
class CartItems {
final String cartItem;
int itemQuantity;
CartItems({this.cartItem, this.itemQuantity: 1});
}
The popmenu itself has no state, so the setState doesn't affect it. The easiest way to fix that is making each item of the menu a Stateful Widget. Something like this:
class CartSelector extends StatefulWidget {
CartSelector({Key key, this.item}) : super(key: key);
final CartItems item;
#override
_CartSelectorState createState() => _CartSelectorState();
}
class _CartSelectorState extends State<CartSelector> {
CartItems item;
#override
void initState() {
super.initState();
item = widget.item;
}
#override
Widget build(BuildContext context) {
return Container(
child: Row(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Flexible(
child: Text(
'${item.cartItem}',
style: TextStyle(color: Colors.grey, fontWeight: FontWeight.w500),
),
),
Spacer(),
GestureDetector(
onTap: () {
setState(() {
item.itemQuantity++;
});
},
child: Icon(
Icons.add_circle,
color: Colors.green,
),
),
Text(
item.itemQuantity.toString(),
style: TextStyle(color: Colors.black),
),
GestureDetector(
onTap: () {
setState(() {
item.itemQuantity--;
});
},
child: Icon(
Icons.remove_circle,
color: Colors.red,
),
),
],
),
);
}
}
And then replace it in the PopMenu builder.
return totalItem.map((item) => PopupMenuItem(
value: item.cartItem,
child: CartSelector(item: item),
)).toList();
This code probably need some polish, but I think it will do the work. Hope this helps.
You have to assign PopupMenuButton.initialValue,
check the simple example:
PopupMenuButton<int>(
itemBuilder: (context) => [
PopupMenuItem(
value: 1,
child: Text("First"),
),
PopupMenuItem(
value: 2,
child: Text("Second"),
),
],
initialValue: 2,
onCanceled: () {
print("You have canceled the menu.");
},
onSelected: (value) {
print("value:$value");
},
icon: Icon(Icons.list),
);
For more information follow this link:
Widgets 14 | PopupMenuButton
Related
class HomeScreen extends StatefulWidget {
final userName;
HomeScreen(this.userName);
#override
State<HomeScreen> createState() => _HomeScreenState();
}
Future<ClientDashboardModel?>? _meterReadingResponse;
BatchSummaryModel? _batchResponse;
AMRDashboardModel? _amrDashboardResponse;
String _userName = '';
int? qcAssignedReads = 0;
int? qcMrCompleted = 0;
int? qcCompletedReads = 0;
int? qcPendingReads = 0;
int? qcApprovedReads = 0;
int? qcRejectedReads = 0;
bool isFirstTimeCalled = false;
Timer? _timer;
int _currentIndex = 0;
final pages = [
const MDMScreen(),
const AMRDashboard(whichScreen: 0),
const AMRDashboard(whichScreen: 1),
const AMRDashboard(whichScreen: 2)
];
class _HomeScreenState extends State<HomeScreen> {
#override
void initState() {
setUpTimedFetch();
super.initState();
if (widget.userName != null) _userName = (widget.userName!);
}
#override
void dispose() {
isFirstTimeCalled = false;
_timer?.cancel();
super.dispose();
}
void setUpTimedFetch() {
if (!isFirstTimeCalled) {
_meterReadingResponse = _getDashboardData();
}
isFirstTimeCalled = true;
}
Future<ClientDashboardModel?> _getDashboardData() async {
ClientDashboardModel? meterReadingResponse;
SharedPreferences prefs = await SharedPreferences.getInstance();
String token = prefs.getString('token')!;
var responseData = await Dashboard().getClientDashboard(token);
if (responseData.statusCode == 200) {
String data = responseData.body;
var decodedData = jsonDecode(data);
meterReadingResponse = null;
meterReadingResponse = ClientDashboardModel.fromJson(decodedData);
return meterReadingResponse;
}
return null;
}
void logout() async {
SharedPreferences.getInstance().then((value) {
value.remove('token');
value.remove('userName');
});
Navigator.pushReplacement(context, MaterialPageRoute(builder: (context) {
return const LoginScreen();
}));
}
Future<void> _showMyDialog() async {
return showDialog<void>(
context: context,
barrierDismissible: false,
builder: (BuildContext context) {
return AlertDialog(
title: const Text(Strings.kLogoutLabel),
content: const Text(Strings.kConfirmLogout),
actions: <Widget>[
TextButton(
child: const Text(Strings.kCancelLabel),
onPressed: () {
Navigator.of(context).pop();
},
),
TextButton(
child: const Text(Strings.kOKLabel),
onPressed: () {
Navigator.of(context).pop();
logout();
},
)
],
);
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
bottomNavigationBar: BottomNavigationBar(
type: BottomNavigationBarType.fixed,
currentIndex: _currentIndex,
backgroundColor: const Color(0XFF116AFF),
unselectedItemColor: Colors.white,
selectedLabelStyle: const TextStyle(color: Colors.white),
showUnselectedLabels: false,
onTap: (value) {
setState(() => _currentIndex = value);
},
items: [
BottomNavigationBarItem(
label: 'MDM',
icon: Container(
decoration: BoxDecoration(
color:
_currentIndex == 0 ? Colors.white : Colors.transparent,
shape: BoxShape.circle),
child: const Padding(
padding: EdgeInsets.all(10.0),
child: Icon(Icons.electric_meter_outlined),
),
),
),
BottomNavigationBarItem(
label: 'Site Survey',
icon: Container(
decoration: BoxDecoration(
color:
_currentIndex == 1 ? Colors.white : Colors.transparent,
shape: BoxShape.circle),
child: const Padding(
padding: EdgeInsets.all(10.0),
child: Icon(Icons.location_city_outlined),
),
),
),
BottomNavigationBarItem(
label: 'Meter Replace',
icon: Container(
decoration: BoxDecoration(
color:
_currentIndex == 2 ? Colors.white : Colors.transparent,
shape: BoxShape.circle),
child: const Padding(
padding: EdgeInsets.all(10.0),
child: Icon(Icons.library_books_outlined),
),
),
),
BottomNavigationBarItem(
label: 'TroubleShoot',
icon: Container(
decoration: BoxDecoration(
color:
_currentIndex == 3 ? Colors.white : Colors.transparent,
shape: BoxShape.circle),
child: const Padding(
padding: EdgeInsets.all(10.0),
child: Icon(Icons.info_outline),
),
),
),
],
),
appBar: AppBar(
automaticallyImplyLeading: false,
title: const Text(Strings.kMRDLabel),
actions: [
IconButton(
onPressed: () {
_showMyDialog();
},
icon: const Icon(Icons.power_settings_new))
],
),
body: childView());
}
Widget childView() {
return SingleChildScrollView(
scrollDirection: Axis.vertical,
child: Padding(
padding: const EdgeInsets.all(20.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
Strings.kWelcome2Label,
style: TextStyle(
color: Colors.amber.shade700,
fontSize: 16.0,
fontWeight: FontWeight.w600),
),
Text(
_userName.toTitleCase(),
style: const TextStyle(
color: Colors.black,
fontSize: 16.0,
fontWeight: FontWeight.w400),
)
],
),
const Spacer(),
ClipRRect(
borderRadius: BorderRadius.circular(10),
child: Container(
padding: const EdgeInsets.all(10),
decoration: BoxDecoration(color: Colors.grey.shade200),
child: SvgPicture.asset(
'images/profile.svg',
height: 54.0,
width: 54.0,
),
),
),
],
),
const SizedBox(
height: 30,
),
Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [pages[_currentIndex]],
),
],
),
));
}
}
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'package:manager/components/dotted_line.dart';
import 'package:manager/model/lookup_cycle.dart';
import 'package:manager/model/mr_report_model.dart';
import 'package:manager/services/dashboard_service.dart';
import 'package:manager/utils/debouncer.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:manager/utils/string_captialize.dart';
class MRPerformance extends StatefulWidget {
const MRPerformance({super.key});
#override
State<MRPerformance> createState() => _MRPerformanceState();
}
bool isFilter = false;
bool isPerformingRequest = false;
int pageNumber = 0;
String _chosenValue = '';
List<MRReportResult> users = [];
Future<List<String>?>? dropDownValue;
ScrollController _scrollController = ScrollController();
final _debouncer = Debouncer(milliseconds: 500);
class _MRPerformanceState extends State<MRPerformance> {
#override
void initState() {
super.initState();
_getMoreData(_chosenValue);
dropDownValue = getAllCategory();
_scrollController.addListener(() {
if (_scrollController.position.pixels ==
_scrollController.position.maxScrollExtent) {
pageNumber++;
_getMoreData(_chosenValue);
}
});
}
Future<List<String>?>? getAllCategory() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
String token = prefs.getString('token')!;
var cycleResponse = await Dashboard().getAllCycle(token);
try {
if (cycleResponse.statusCode == 200) {
List<String> items = [];
var jsonData = json.decode(cycleResponse.body) as List;
List<LookUpCycle> lookupCycle = jsonData
.map<LookUpCycle>((json) => LookUpCycle.fromJson(json))
.toList();
items.add('Select');
for (var element in lookupCycle) {
if (element.name != null) {
items.add(element.name!);
}
}
return items;
}
} catch (ex) {
throw (ex.toString());
}
return null;
}
#override
void dispose() {
super.dispose();
_scrollController.dispose();
}
void _getMoreData(String searchCycle) async {
List<MRReportResult>? resultResponse = [];
if (!isPerformingRequest) {
setState(() {
isPerformingRequest = true;
});
SharedPreferences prefs = await SharedPreferences.getInstance();
String token = prefs.getString('token')!;
debugPrint(token);
var responseData =
await Dashboard().getMRReport(token, pageNumber, 10, searchCycle);
if (responseData.statusCode == 200) {
String data = responseData.body;
var decodedData = jsonDecode(data);
MRReportModel newEntries = MRReportModel.fromJson(decodedData);
if (newEntries.result == null) {
if (newEntries.result!.isEmpty) {
double edge = 50.0;
double offsetFromBottom =
_scrollController.position.maxScrollExtent -
_scrollController.position.pixels;
if (offsetFromBottom < edge) {
_scrollController.animateTo(
_scrollController.offset - (edge - offsetFromBottom),
duration: const Duration(milliseconds: 500),
curve: Curves.easeOut);
}
}
}
setState(() {
users.addAll(newEntries.result!);
isPerformingRequest = false;
});
}
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
resizeToAvoidBottomInset: false,
appBar: AppBar(title: const Text('MR Performance')),
body: filterView());
}
void setFilterState() {
setState(() {
if (isFilter == true) {
isFilter = false;
} else {
isFilter = true;
}
});
}
Widget _buildProgressIndicator() {
return Padding(
padding: const EdgeInsets.all(8.0),
child: Center(
child: Opacity(
opacity: isPerformingRequest ? 1.0 : 0.0,
child: const CircularProgressIndicator(),
),
),
);
}
Widget filterView() {
return Column(
children: [
Row(
children: [
Visibility(
visible: isFilter ? true : false,
child: Flexible(
child: Card(
shape: RoundedRectangleBorder(
side: const BorderSide(color: Color(0XFFDCDCDC)),
borderRadius: BorderRadius.circular(10)),
elevation: 2,
child: Column(
children: [
Row(
children: [
Expanded(
child: TextField(
textInputAction: TextInputAction.search,
decoration: const InputDecoration(
border: InputBorder.none,
prefixIcon: InkWell(
child: Icon(Icons.search),
),
contentPadding: EdgeInsets.all(8.0),
hintText: 'Search ',
),
onChanged: (string) {
_debouncer.run(() {});
},
),
),
],
),
],
),
),
),
),
Visibility(
visible: isFilter ? false : true,
child: Flexible(
child: Card(
elevation: 4,
shape: RoundedRectangleBorder(
side: const BorderSide(color: Color(0XFFDCDCDC)),
borderRadius: BorderRadius.circular(10)),
child: Padding(
padding: const EdgeInsets.only(
left: 10,
),
child: FutureBuilder<List<String>?>(
future: dropDownValue,
builder: (context, snapshot) {
if (snapshot.connectionState ==
ConnectionState.done) {
if (snapshot.hasError) {
return const Text('Something wrong');
} else if (snapshot.hasData) {
var data = snapshot.data!;
return DropdownButtonHideUnderline(
child: DropdownButton<String>(
icon: const Icon(
Icons.expand_more_outlined,
size: 35,
color: Color(0XFF116AFF),
),
borderRadius: BorderRadius.circular(10),
isExpanded: true,
// value: _chosenValue.isNotEmpty ? _chosenValue : null,
elevation: 16,
style: const TextStyle(
color: Colors.black,
fontSize: 14,
fontWeight: FontWeight.w400),
items: data.map((String value) {
return DropdownMenuItem(
value: value, child: Text(value));
}).toList(),
hint: Padding(
padding: const EdgeInsets.all(15),
child: Text(
_chosenValue.isEmpty
? 'Cycle'
: _chosenValue,
style: const TextStyle(
color: Colors.black,
fontSize: 16,
fontWeight: FontWeight.w400),
),
),
onChanged: (String? value) {
setState(() {
if (value != null) {
pageNumber = 0;
users.clear();
if (value == 'Select') {
_chosenValue = '';
} else {
_chosenValue = value;
}
_getMoreData(_chosenValue);
}
});
},
),
);
}
}
return const CircularProgressIndicator();
},
)),
),
),
),
InkWell(
onTap: () => {setFilterState()},
child: Card(
elevation: 4,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10)),
child: Padding(
padding: const EdgeInsets.all(10.0),
child: isFilter
? const Icon(Icons.filter_alt, color: Color(0XFF116AFF))
: const Icon(
Icons.search,
color: Color(0XFF116AFF),
),
),
),
),
],
),
Expanded(
child: ListView.builder(
shrinkWrap: true,
scrollDirection: Axis.vertical,
itemCount: users.length + 1,
controller: _scrollController,
itemBuilder: (BuildContext context, int index) {
if (index == users.length) {
return _buildProgressIndicator();
} else {
return cardView(users, index);
}
}),
),
],
);
}
Widget cardView(List<MRReportResult>? users, int index) {
return Card(
shape: RoundedRectangleBorder(
side: const BorderSide(color: Color(0XFFDCDCDC)),
borderRadius: BorderRadius.circular(10)),
margin: const EdgeInsets.all(8),
child: Padding(
padding: const EdgeInsets.all(10.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Container(
decoration: BoxDecoration(
border: Border.all(
color: Colors.orange,
),
borderRadius: const BorderRadius.all(Radius.circular(20))),
margin: const EdgeInsets.all(10),
padding: const EdgeInsets.all(10),
child: Row(
children: [
Text(
'${users?[index].mRNumber}: ',
style: const TextStyle(
color: Colors.orange,
fontSize: 10,
fontWeight: FontWeight.w600),
),
Text(
'${users?[index].meterReaderName}'.toTitleCase(),
style: const TextStyle(
color: Colors.black,
fontSize: 10,
fontWeight: FontWeight.w400),
),
],
),
),
Padding(
padding: const EdgeInsets.only(left: 20),
child: Row(
children: [
const Text(
'Supervisor: ',
style: TextStyle(
color: Color(0XFF004AC6),
fontSize: 10,
fontWeight: FontWeight.w600),
),
Expanded(
child: Text(
'${users?[index].supervisor}'.toTitleCase(),
overflow: TextOverflow.fade,
style: const TextStyle(
color: Colors.black,
fontSize: 10,
fontWeight: FontWeight.w400),
),
),
],
),
),
const SizedBox(height: 10),
const MySeparator(),
const SizedBox(height: 10),
Padding(
padding: const EdgeInsets.only(left: 30, right: 30),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
children: [
Column(
children: [
Text(
'${users?[index].unreadPercenatge}%',
style: const TextStyle(
color: Colors.orange,
fontSize: 10,
fontWeight: FontWeight.w600),
),
const Text(
'Unread',
style: TextStyle(
color: Color(0XFF004AC6),
fontSize: 12,
fontWeight: FontWeight.w400),
)
],
)
],
),
Row(
children: [
Column(
children: [
Text(
'${users?[index].readPercenatge}%',
style: const TextStyle(
color: Colors.green,
fontSize: 10,
fontWeight: FontWeight.w600),
),
const Text(
'Read',
style: TextStyle(
color: Color(0XFF004AC6),
fontSize: 12,
fontWeight: FontWeight.w400),
)
],
)
],
),
Row(
children: [
Column(
children: [
Text(
'${users?[index].plusorMinusTwoReadPercentage}%',
style: const TextStyle(
color: Colors.green,
fontSize: 10,
fontWeight: FontWeight.w600),
),
const Text(
'+/- 2',
style: TextStyle(
color: Color(0XFF004AC6),
fontSize: 12,
fontWeight: FontWeight.w400),
)
],
)
],
),
Row(
children: [
Column(
children: [
Text(
'${users?[index].above32ReadPercentage}%',
style: const TextStyle(
color: Colors.orange,
fontSize: 10,
fontWeight: FontWeight.w600),
),
const Text(
'>32',
style: TextStyle(
color: Color(0XFF004AC6),
fontSize: 12,
fontWeight: FontWeight.w400),
)
],
)
],
),
],
),
),
],
),
),
);
}
}
════════ Exception caught by widgets library ═══════════════════════════════════
The following assertion was thrown building Builder:
A ScrollController was used after being disposed.
Once you have called dispose() on a ScrollController, it can no longer be used.
The relevant error-causing widget was
MaterialApp
lib/main.dart:13
When the exception was thrown, this was the stack
You have declared _getMoreData two times in init(), so remove it before _scrollController as shown below
void initState() {
super.initState();
_getMoreData(_chosenValue);-------------> remove this from here
dropDownValue = getAllCategory();
_scrollController.addListener(() {
if (_scrollController.position.pixels ==
_scrollController.position.maxScrollExtent) {
pageNumber++;
_getMoreData(_chosenValue);
}
});
}
OR
Declare ScrollController _scrollController = ScrollController(); in state class _MRPerformanceState as below code:
class _MRPerformanceState extends State<MRPerformance> {
ScrollController _scrollController = ScrollController();
when i navigate from NewSales screen to CreateItem screen and add item and press the add button
the item is added to sqflite database then it navigates back to new sales but the state is not updated , it's updated only if i restarted the app
the NewSales screen
class _NewSalesState extends State<NewSales> {
final controller = TextEditingController();
showAlertDialog(BuildContext context,String name) {
// Create button
// Create AlertDialog
AlertDialog alert = AlertDialog(
title: const Text("Alert"),
content: const Text("you want to delete this item?"),
actions: [
TextButton(
style: ButtonStyle(
backgroundColor: MaterialStateProperty.all(Colors.green)),
child: const Text("CANCEL", style: TextStyle(color: Colors.white)),
onPressed: () {
Navigator.of(context).pop();
},
),
BlocBuilder<SalesCubit, SalesState>(
builder: (context, state) {
final bloc=BlocProvider.of<SalesCubit>(context);
return TextButton(
style: ButtonStyle(
backgroundColor: MaterialStateProperty.all(Colors.red)),
child: const Text(
"DELETE",
style: TextStyle(color: Colors.white),
),
onPressed: () {
Navigator.of(context).pop();
bloc.deleteItem(name).then((value) {
bloc.getAllItems();
});
},
);
},
)
],
);
// show the dialog
showDialog(
context: context,
builder: (BuildContext context) {
return alert;
},
);
}
// #override
#override
Widget build(BuildContext context) {
final deviceSize = MediaQuery
.of(context)
.size;
return BlocConsumer<SalesCubit, SalesState>(
listener: (context, state) {},
builder: (context, state) {
final bloc = BlocProvider.of<SalesCubit>(context);
if (state is SalesInitial) {
bloc.getAllItems();
}
return Scaffold(
drawer: const navDrawer(),
appBar: AppBar(
title: const Text("Ticket"),
actions: [
IconButton(
onPressed: () async {
String barcodeScanRes;
// Platform messages may fail, so we use a try/catch PlatformException.
try {
barcodeScanRes = await FlutterBarcodeScanner.scanBarcode(
'#ff6666', 'Cancel', true, ScanMode.BARCODE);
print('barcodeScanRes $barcodeScanRes');
print(bloc.bsResult);
} on PlatformException {
barcodeScanRes = 'Failed to get platform version.';
}
// If the widget was removed from the tree while the asynchronous platform
// message was in flight, we want to discard the reply rather than calling
// setState to update our non-existent appearance.
if (!mounted) return;
bloc.toggleSearch();
controller.text = barcodeScanRes;
bloc.filterItems(barcodeScanRes);
},
icon: const Icon(Icons.scanner),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: IconButton(
onPressed: () {
Navigator.of(context).pushNamed(Routes.addCustomerRoute);
},
icon: Icon(Icons.person_add)),
),
],
),
body: Column(
children: [
// the first button
Container(
width: double.infinity,
height: deviceSize.height * .1,
color: Colors.grey,
child: GestureDetector(
onTap: ()=>displayMessage("an item was charged successfully", context),
child: Padding(
padding: const EdgeInsets.all(10),
child: Container(
color: Colors.green,
child: const Center(
child: Text("charge",style: TextStyle(color: Colors.white),),
),
),
),
),
),
// the second container
SizedBox(
width: deviceSize.width,
height: deviceSize.height * .1,
child: Row(
children: [
DecoratedBox(
decoration:
BoxDecoration(border: Border.all(color: Colors.grey)),
child: SizedBox(
width: deviceSize.width * .8,
child: bloc.isSearch
? TextFormField(
autofocus: true,
controller: controller,
decoration:
const InputDecoration(hintText: "Search"),
onChanged: (value) {
bloc.filterItems(controller.text);
},
)
: DropdownButtonFormField<String>(
// underline: Container(),
// value: "Discounts",
hint: const Padding(
padding: EdgeInsets.only(left: 10),
child: Text('Please choose type'),
),
items: <String>['Discounts', 'All Items']
.map((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
}).toList(),
onChanged: (_) {},
),
),
),
DecoratedBox(
decoration:
BoxDecoration(border: Border.all(color: Colors.grey)),
child: SizedBox(
width: deviceSize.width * .2,
child: IconButton(
onPressed: () {
bloc.toggleSearch();
if (!bloc.isSearch) {
bloc.filterItems('');
}
},
icon: Icon(
!bloc.isSearch ? Icons.search : Icons.close)),
),
)
],
),
),
// the third container
if (state is IsLoading || state is SalesInitial)
const Center(
child: CircularProgressIndicator(
color: Colors.green,
backgroundColor: Colors.green,
),
),
if (state is Loaded || state is IsSearch || state is SearchDone)
bloc.items.isEmpty
? const Center(
child: Text("no items added yet"),
)
: Expanded(
child: bloc.filteredItems.isEmpty
? const Center(
child: Text(
"no items found with this name",
style: TextStyle(color: Colors.green),
),
)
: ListView.builder(
itemCount: bloc.filteredItems.length,
itemBuilder: (context, i) {
final item = bloc.filteredItems[i];
return Card(
child: ListTile(
leading: CircleAvatar(
backgroundColor: Colors.green,
radius: 20,
child: Text(item.price),
),
title: Text(item.name),
subtitle: Text(item.barcode),
trailing: Column(
children: [
IconButton(
onPressed: () {
showAlertDialog(context,item.name);
// bloc.deleteItem(item.name).then((value) {
// bloc.getAllItems();
// });
},
icon: const Icon(
Icons.delete,
color: Colors.red,
))
],
)),
);
}))
],
),
floatingActionButton: FloatingActionButton(
child: const Icon(Icons.add),
onPressed: ()async {
Navigator.of(context).pushNamed(Routes.createItemRoute);
},
),
);
},
);
}
}
the CreateItem screen
class _CreateItemState extends State<CreateItem> {
final nameController = TextEditingController();
final priceController = TextEditingController();
final costController = TextEditingController();
final skuController = TextEditingController();
final barcodeController = TextEditingController();
final inStockController = TextEditingController();
bool each = true; // bool variable for check box
bool isColor = true;
bool switchValue = false;
File? file;
String base64File = "";
String path = "";
final _formKey = GlobalKey<FormState>();
#override
void dispose() {
nameController.dispose();
priceController.dispose();
costController.dispose();
skuController.dispose();
barcodeController.dispose();
inStockController.dispose();
super.dispose();
}
Future pickImage(ImageSource source) async {
File? image1;
XFile imageFile;
final imagePicker = ImagePicker();
final image = await imagePicker.pickImage(source: source);
imageFile = image!;
image1 = File(imageFile.path);
List<int> fileUnit8 = image1.readAsBytesSync();
setState(() {
base64File = base64.encode(fileUnit8);
});
}
#override
Widget build(BuildContext context) {
final deviceSize = MediaQuery.of(context).size;
return BlocProvider(
create: (BuildContext context) => CreateItemCubit(),
child: Builder(builder: (context){
final bloc=BlocProvider.of<CreateItemCubit>(context);
return Scaffold(
appBar: AppBar(
leading: IconButton(
onPressed: () => Navigator.of(context).pushNamedAndRemoveUntil(Routes.newSalesRoute, (route) => false),
icon: const Icon(Icons.arrow_back)),
title: const Text("Create Item"),
actions: [TextButton(onPressed: () {}, child: const Text("SAVE"))],
),
body: Padding(
padding: const EdgeInsets.all(10),
child: Form(
key: _formKey,
child: ListView(
children: [
TextFormField(
controller: nameController,
cursorColor: ColorManager.black,
decoration: const InputDecoration(hintText: "Name"),
validator: (value) {
if (value!.isEmpty || value.length < 5) {
return "name can't be less than 5 chars";
}
return null;
},
),
gapH24,
Text(
"Category",
style: TextStyle(color: ColorManager.grey),
),
SizedBox(
width: deviceSize.width,
child: DropdownButtonFormField<String>(
// value: "Categories",
hint: const Padding(
padding: EdgeInsets.only(left: 10),
child: Text('No Category'),
),
items: <String>['No Category', 'Create Category']
.map((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
}).toList(),
onChanged: (_) {},
),
),
gapH24,
const Text(
"Sold by",
style: TextStyle(color: Colors.black),
),
gapH24,
Row(
children: [
Checkbox(
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.all(
Radius.circular(5.0))), // Rounded Checkbox
value: each,
onChanged: (inputValue) {
setState(() {
each = !each;
});
},
),
gapW4,
const Text(
"Each",
style: TextStyle(fontWeight: FontWeight.bold),
),
],
),
gapH24,
Row(
children: [
Checkbox(
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.all(
Radius.circular(5.0))), // Rounded Checkbox
value: !each,
onChanged: (inputValue) {
setState(() {
each = !each;
});
},
),
gapW4,
const Text(
"Weight",
style: TextStyle(fontWeight: FontWeight.bold),
),
],
),
TextFormField(
controller: priceController,
decoration: InputDecoration(
hintText: "Price",
hintStyle: TextStyle(color: ColorManager.grey)),
validator: (value) {
if (value!.isEmpty) {
return "price can,t be empty";
}
return null;
},
),
gapH4,
Text(
"leave the field blank to indicate price upon sale",
style: TextStyle(color: ColorManager.grey),
),
gapH20,
Text(
"Cost",
style: TextStyle(color: ColorManager.grey),
),
TextFormField(
controller: costController,
decoration: InputDecoration(
hintText: "cost",
hintStyle: TextStyle(color: ColorManager.black)),
validator: (value) {
if (value!.isEmpty) {
return "cost can't be empty";
}
return null;
},
),
gapH20,
Text(
"SKU",
style: TextStyle(color: ColorManager.grey),
),
TextFormField(
controller: skuController,
decoration: InputDecoration(
hintText: "Sku",
hintStyle: TextStyle(color: ColorManager.black)),
validator: (value) {
if (value!.isEmpty) {
return "Sku can't be empty";
}
return null;
},
),
gapH30,
TextFormField(
controller: barcodeController,
decoration: InputDecoration(
hintText: "Barcode",
hintStyle: TextStyle(color: ColorManager.grey)),
validator: (value) {
if (value!.isEmpty) {
return "Barcode can't be empty";
}
return null;
},
),
gapH30,
// Divider(thickness: 1,color: ColorManager.black,),
Text(
"Inventory",
style: TextStyle(
color: ColorManager.green,
fontSize: 15,
fontWeight: FontWeight.bold),
),
// ListTile(
// title: Text("TrackStock",style: TextStyle(color: ColorManager.green,fontSize: 15,fontWeight: FontWeight.bold)),
// trailing: Switch(value: switchValue, onChanged: (bool value) {
// setState(() {
// switchValue=!switchValue;
// });
// },),
// ),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text("TrackStock",
style: TextStyle(
color: ColorManager.grey,
fontSize: 15,
fontWeight: FontWeight.bold)),
Switch(
value: switchValue,
onChanged: (bool value) {
setState(() {
switchValue = !switchValue;
});
},
),
],
),
gapH10,
if (switchValue)
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"in stock",
style:
TextStyle(color: ColorManager.grey, fontSize: 15),
),
TextFormField(
keyboardType: TextInputType.number,
controller: inStockController,
decoration: const InputDecoration(hintText: "0"),
validator: (value) {
if (value!.isEmpty) {
return "value can't be empty";
}
return null;
},
)
],
),
gapH20,
Text(
"Representation in POS",
style: TextStyle(
color: ColorManager.green,
fontSize: 15,
fontWeight: FontWeight.bold),
),
gapH15,
Row(
children: [
Checkbox(
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.all(
Radius.circular(5.0))), // Rounded Checkbox
value: isColor,
onChanged: (inputValue) {
setState(() {
if (!isColor) {
isColor = !isColor;
}
});
},
),
gapW4,
const Text(
"Color and Shape",
style: TextStyle(fontWeight: FontWeight.bold),
),
],
),
gapH10,
Row(
children: [
Checkbox(
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.all(
Radius.circular(5.0))), // Rounded Checkbox
value: !isColor,
onChanged: (inputValue) {
setState(() {
if (isColor) {
isColor = !isColor;
}
});
},
),
gapW4,
const Text(
"Image",
style: TextStyle(fontWeight: FontWeight.bold),
),
],
),
isColor
? GridView.builder(
physics:
const NeverScrollableScrollPhysics(), // to disable GridView's scrolling
shrinkWrap: true, // You won't see infinite size error
gridDelegate:
const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 4,
crossAxisSpacing: 5.0,
mainAxisSpacing: 5.0,
),
itemCount: containers().length,
itemBuilder: (BuildContext context, int index) {
return containers()[index];
},
)
: Padding(
padding: const EdgeInsets.all(10),
child: Row(
children: [
Expanded(
flex: 1,
child: base64File == ""
? const Icon(Icons.person)
: Container(
decoration: BoxDecoration(
borderRadius:
BorderRadius.circular(5)),
child: Image.memory(
base64.decode(base64File)))),
gapW4,
Expanded(
flex: 2,
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
TextButton(
onPressed: () =>
pickImage(ImageSource.camera),
child: Row(
children: [
Icon(
Icons.camera_alt,
color: ColorManager.black,
),
gapW8,
Text(
"Take a photo",
style: TextStyle(
color: ColorManager.black,
),
)
],
),
),
const Divider(
thickness: 1,
color: Colors.grey,
),
TextButton(
onPressed: () =>
pickImage(ImageSource.gallery),
child: Row(
children: [
Icon(Icons.camera_alt,
color: ColorManager.black),
gapW8,
Text(
"Choose from gallery",
style: TextStyle(
color: ColorManager.black,
),
)
],
),
)
],
),
)
],
),
)
],
),
),
),
floatingActionButton: FloatingActionButton(
child: const Icon(Icons.save,color: Colors.white,),
onPressed: () async {
bool isValid = _formKey.currentState!.validate();
if (isValid) {
FocusScope.of(context).unfocus();
ItemModel? item=await bloc.checkItem(nameController.text);
if(item!=null){
displayMessage("this item was inserted before", context);
}else{
int? res=await bloc.saveItem(ItemModel(
nameController.text,
priceController.text,
costController.text,
skuController.text,
barcodeController.text));
if(res!=null){
displayMessage("an item was inserted successfully", context);
Navigator.of(context).pushAndRemoveUntil(MaterialPageRoute(builder: (context)=>const NewSales()), (route) => false);
}
}
}
},
),
);
}
),
);
}
}
this is the SaleCubit
class SalesCubit extends Cubit<SalesState> {
SalesCubit() : super(SalesInitial());
bool isSearch=false;
ItemDBHelper db=ItemDBHelper();
List<ItemModel> items=[];
List<ItemModel> filteredItems=[];
String bsResult='Unknown'; //barcode search result
void toggleSearch(){
isSearch=!isSearch;
emit(IsSearch());
}
void setBarcodeResult(String value){
bsResult=value;
emit(SearchDone());
}
void filterItems(String query){
// emit(Searching());
query.isEmpty?
filteredItems=items:
filteredItems=items.where((item) => item.name.toLowerCase().contains(query.toLowerCase())).toList();
emit(SearchDone());
}
Future<List<ItemModel>?> getAllItems()async{
try{
emit(IsLoading());
items=await db.getAllItems();
filteredItems=items;
print(items);
return items;
}finally{
emit(Loaded());
}
}
Future<void> deleteItem(String name)async{
await db.deleteItem(name);
emit(SearchDone());
}
}
this is the SalesState
part of 'sales_cubit.dart';
#immutable
abstract class SalesState {}
class SalesInitial extends SalesState {}
class IsSearch extends SalesState {}
class IsLoading extends SalesState {}
class Loaded extends SalesState {}
class Searching extends SalesState {}
class SearchDone extends SalesState {}
When the name is already there
Without any change in the text field when clicking its showing
I was trying to update the name in firebase, it was working fine, but each time when I'm clicking edit without any change it's uploading to firebase. I don't want that to happen. Please help me out
Here is my code:
`import 'package:flutter/material.dart';
class EditProfile extends StatefulWidget {
#override
_EditProfileState createState() => _EditProfileState();
}
class _EditProfileState extends State<EditProfile> {
final TextEditingController nameController = TextEditingController();
final _keyChange = GlobalKey<FormState>();
bool btnTextChange = false;
TextEditingController _editingController = TextEditingController(text:"Johny Boy");
String initialText = "Johny Boy";
Widget _editTitleContainer() {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
margin: EdgeInsets.only(left: 20,top: 20,bottom: 20),
child: Text(
"Name",
),
),
Container(
decoration: new BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(10.0)),
color: Colors.white,
),
width: MediaQuery.of(context).size.width,
margin: EdgeInsets.symmetric(horizontal: 20),
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 20),
child:
Form(
key: _keyChange,
child: TextField(
enableInteractiveSelection: true,
onSubmitted: (newValue){
setState(() {
initialText = newValue;
});
},
onChanged: (String? value) {
value!.isEmpty ? changeButtonColor(false) : changeButtonColor(true);
},
controller: _editingController,
decoration: InputDecoration(
border: InputBorder.none,
),)
)
),
],
);
}
#override
void dispose() {
_editingController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("sample"),
),
body: SafeArea(
child: Container(
child: Column(
children: [
_editTitleContainer(),
Container(
alignment: Alignment.center,
margin:EdgeInsets.only(top: 20),
child: ElevatedButton(
style: ElevatedButton.styleFrom(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(15.0),
),
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 20),
),
onPressed: () {
if (_keyChange.currentState!.validate()) {
_keyChange.currentState!.save();
successMessage();
}
},
child: Text(
"EDIT",
style: TextStyle(
color: Color(0xFF527DAA),
letterSpacing: 1.5,
fontSize: 15,
fontWeight: FontWeight.bold,
fontFamily: 'OpenSans',
),
),
),
),
],
),
),
),
);
}
void successMessage() {
String name = _editingController.text.trim();
if (name.isNotEmpty) {
setState(() {
showDialog(
context: context,
builder: (ctx) {
return AlertDialog(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16)),
title: Text("Name Changed Success"),
actions: [
TextButton(
child: Text("Ok"),
onPressed: () {
Navigator.of(ctx).pop();
},
),
],
);
},
);
});
}else {
setState(() {
showDialog(
context: context,
builder: (ctx) {
return AlertDialog(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16)),
title: Text("Error!"),
content: Text("Enter Your Name"),
actions: [
TextButton(
child: Text("Close"),
onPressed: () {
Navigator.of(ctx).pop();
},
),
],
);
},
);
});
}
}
}`.
You only need to compare the initial text and the text of _editingController. If they are not the same, then you can update and show successMessage. It will look like this:
ElevatedButton(
style: ElevatedButton.styleFrom(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(15.0),
),
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 20),
),
onPressed: () {
if (_keyChange.currentState!.validate()) {
// HERE!
if (initialText != _editingController.text) {
_keyChange.currentState!.save();
successMessage();
}
}
},
I'm trying to update the suffixIcon in Form for TextFormField's InputDecoration , to check the length of the giving String by onChange function ,but no update . In add I did test on Textcontroller to give it value when the length >= 8 it's worked ,I wish I give a good idea for the issue . thanks in advance for your solution .
import 'package:flutter/material.dart';
bulidDialogChangePass() {
return DialogPass();
}
class DialogPass extends StatefulWidget {
const DialogPass({Key? key}) : super(key: key);
#override
State<StatefulWidget> createState() {
return DialogPassState();
}
}
class DialogPassState extends State<DialogPass> {
TextEditingController _fPasswordValue = TextEditingController();
TextEditingController _sPasswordValue = TextEditingController();
final _formKey = GlobalKey<FormState>();
ValueChanged<String>? onChanged;
bool _showIcons = false;
bool _startValidateFPass = false;
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.only(bottom: 25.0),
child: Wrap(
children: [
Container(
child: Row(
children: [
Text("Password :"),
TextButton(
onPressed: () {
_showDialog(context);
},
child: Wrap(
direction: Axis.vertical,
alignment: WrapAlignment.end,
children: [Text("Change")],
),
)
],
),
),
],
),
);
}
_showDialog(BuildContext context) async {
var size = MediaQuery.of(context).size;
return await showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
content: Stack(
clipBehavior: Clip.none,
children: <Widget>[
Form(
key: _formKey,
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Padding(
padding: EdgeInsets.all(8.0),
child: TextFormField(
controller: _fPasswordValue,
onChanged: (value) {_onChange(_fPasswordValue.text);},
decoration: InputDecoration(
labelText: "New Password",
suffixIcon: _showIcons && _startValidateFPass ? _setIcon() :null,
),
),
),
Padding(
padding: EdgeInsets.all(8.0),
child: TextFormField(
controller: _sPasswordValue,
decoration: InputDecoration(
labelText: "Confirm Password",
suffixIcon: _showIcons && _startValidateFPass ? _setIcon() :null, // I will change to check aothers soon
),
),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: Wrap(
children: [
OutlinedButton(
style: OutlinedButton.styleFrom(
fixedSize: Size(size.width / 2 - 10, 20),
padding: EdgeInsets.symmetric(horizontal: 50),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20)),
),
onPressed: () {},
child: Text("CANCEL",
style: TextStyle(
fontSize: 14,
letterSpacing: 2.2,
color: Colors.black)),
),
OutlinedButton(
style: OutlinedButton.styleFrom(
fixedSize: Size(size.width / 2 - 10, 20),
backgroundColor: Colors.blue[400],
primary: Colors.black,
padding: EdgeInsets.symmetric(horizontal: 50),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20)),
),
onPressed: () {},
child: Text("Save",
style: TextStyle(
fontSize: 14,
letterSpacing: 2.2,
color: Colors.black)),
),
],
),
),
],
),
),
],
),
);
});
}
_onChange(String value) {
setState(() {
_startValidateFPass = true;
if (value.length >= 8) {
_sPasswordValue.text = "test"; // test to check setState
_showIcons = true;
}
});
}
Icon _setIcon() {
Icon icon;
if(_showIcons){
_sPasswordValue.text = "test"; // test to check setState
print('dfsdfsd');
icon = Icon(
Icons.done_all,
color: Colors.green,
);
}else{
icon = Icon(
Icons.highlight_off,
color: Colors.red,
);
}
return icon;
}
}
Make another stateful widget and make Alertdialog as its child or use stateful builder to accomplish this as
showDialog(
context: context,
builder: (context) {
String contentText = "Your Dialog";
return StatefulBuilder(
builder: (context, setState) {
return AlertDialog(
title: Text("Title"),
content: Text(contentText),
actions: <Widget>[
TextButton(
onPressed: () => Navigator.pop(context),
child: Text("Cancel"),
),
TextButton(
onPressed: () {
setState(() {
contentText = "Changed state";
});
},
child: Text("Change"),
),
],
);
},
);
},
);
I am making a shopping cart app in flutter with Change Notifier Provider. It is working fast and well at first. But it takes the more memory, the more I use it. I am adding and removing clearing items from list. Nothing complicated. Cannot know why it is working like this. It is slowing down gradually. This is main.dart:
class Main extends StatelessWidget {
#override
Widget build(BuildContext context) {
return ChangeNotifierProvider(
create: (context) => Cart(),
child: MaterialApp(
title: 'Flutter Demo',
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: swatchColor,
primaryColor: primaryColor,
accentColor: tertiaryColor,
textSelectionTheme: TextSelectionThemeData(
cursorColor: Colors.black,
selectionColor: primaryColor,
selectionHandleColor: primaryColor,
),
),
initialRoute: '/login',
routes: {
'/home': (context) => Home(),
'/login': (context) => Login(),
'/tableReview': (context) => TableReview(),
'/createOrder': (context) => CreateOrder(),
'/orderReview': (context) => OrderReview(),
},
),
);
}
}
This is main cart_model.dart
import 'package:counter/data/models/cart_item.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart';
class Cart extends ChangeNotifier {
List<Item> cart = [];
addItemToCart({#required item}) {
Item newItem = Item(
id: item.id,
price: item.price is int ? item.price : int.parse(item.price),
title: item.title,
);
int i = cart.indexWhere((previousItem) => previousItem.id == newItem.id);
if (i > -1) {
cart[i].count++;
} else {
cart.add(newItem);
}
notifyListeners();
}
removeItemFromCart({#required item}) {
int index = cart.indexWhere((element) => element.id == item.id);
if (index > -1) {
if (cart[index].count > 1) {
cart[index].count--;
} else {
cart.removeWhere((element) => element.id == item.id);
}
}
notifyListeners();
}
clearCart() {
cart.clear();
notifyListeners();
}
int get totalPrice =>
cart.fold(0, (total, current) => total + (current.price * current.count));
}
This is menuitem.dart
class MenuItem extends StatelessWidget {
final dynamic foodItem;
MenuItem({#required this.foodItem});
void _showSecondPage(BuildContext context) {
Navigator.of(context).push(
MaterialPageRoute(
builder: (ctx) => Scaffold(
backgroundColor: Colors.black45,
body: Center(
child: Hero(
tag: foodItem.imgPath,
child: Image(image: NetworkImage(base_url + foodItem.imgPath)),
),
),
),
),
);
}
#override
Widget build(BuildContext context) {
int itemIndex = Provider.of<Cart>(context, listen: false)
.cart
.indexWhere((item) => item.id == foodItem.id);
return Container(
height: 80,
padding: EdgeInsets.symmetric(horizontal: 10, vertical: 10),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
GestureDetector(
onTap: () => _showSecondPage(context),
child: Hero(
tag: foodItem.imgPath,
child: CircleAvatar(
backgroundImage: NetworkImage(base_url + foodItem.imgPath),
),
),
),
SizedBox(
width: 15,
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Text(
foodItem.title,
style: TextStyle(
fontWeight: FontWeight.w700,
fontSize: 17,
),
),
Text('${foodItem.price.toString()} сум'),
],
)
],
),
Container(
width: 160,
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
IconButton(
icon:
Icon(Icons.remove, color: Theme.of(context).primaryColor),
onPressed: () {
Provider.of<Cart>(context, listen: false)
.addItemToCart(item: foodItem);
},
),
Text(
itemIndex > -1
? '${Provider.of<Cart>(context).cart[itemIndex].count}'
: '0',
style: TextStyle(fontSize: 18),
),
IconButton(
icon: Icon(Icons.add, color: Theme.of(context).primaryColor),
onPressed: () {
Provider.of<Cart>(context, listen: false)
.addItemToCart(item: foodItem);
},
),
],
),
),
],
),
);
}
}
This is consumer part:
import 'package:counter/common/socketio.dart';
import 'package:counter/data/models/cart_model.dart';
import 'package:counter/presentation/widgets/empty_cart.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:shared_preferences/shared_preferences.dart';
class OrderReview extends StatefulWidget {
#override
_OrderReviewState createState() => _OrderReviewState();
}
class _OrderReviewState extends State<OrderReview> {
bool isCommentEnabled = false;
String guestCount;
String comment = '';
final _formKey = GlobalKey<FormState>();
#override
Widget build(BuildContext context) {
final myCart = context.watch<Cart>();
return Scaffold(
resizeToAvoidBottomInset: true,
appBar: AppBar(
title: Text('Проверка заказа'),
),
body: Builder(
builder: (context) {
if (myCart.cart.length > 0) {
handleOrderCreate() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
String userId = prefs.getString('userId');
String tableNum = prefs.getString('tableNum');
String status = 'accepted';
print('$userId, $tableNum, $status');
Map orderToCreate = {
'tableNum': tableNum,
'status': 'accepted',
'userId': userId,
'guestCount': guestCount,
'comment': comment,
'foodList': myCart.cart
};
socketIO.emit('createOrder', json.encode(orderToCreate));
Navigator.of(context).pushNamed('/home');
Provider.of<Cart>(context, listen: false).clearCart();
}
return GestureDetector(
onTap: () => FocusScope.of(context).requestFocus(new FocusNode()),
child: LayoutBuilder(
builder:
(BuildContext context, BoxConstraints viewportConstraints) {
return SingleChildScrollView(
child: ConstrainedBox(
constraints: BoxConstraints(
minHeight: viewportConstraints.maxHeight,
),
child: IntrinsicHeight(
child: Container(
padding: const EdgeInsets.symmetric(
horizontal: 8.0, vertical: 8.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Form(
key: _formKey,
child: Column(
children: [
Container(
height: 350,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.only(
topLeft: Radius.circular(10),
topRight: Radius.circular(10),
bottomLeft: Radius.circular(10),
bottomRight: Radius.circular(10)),
boxShadow: [
BoxShadow(
color:
Colors.grey.withOpacity(0.5),
spreadRadius: 5,
blurRadius: 7,
offset: Offset(0,
3), // changes position of shadow
),
]),
child: ListView.separated(
itemBuilder:
(BuildContext context, int i) {
return ListTile(
title: Text(myCart.cart[i].title),
trailing: Container(
width: 160,
child: Row(
crossAxisAlignment:
CrossAxisAlignment.center,
mainAxisAlignment:
MainAxisAlignment
.spaceBetween,
children: <Widget>[
IconButton(
icon: Icon(
Icons.remove,
color: Theme.of(context)
.primaryColor,
),
onPressed: () {
Provider.of<Cart>(context,
listen: false)
.removeItemFromCart(
item: myCart
.cart[i]);
},
),
Text(
'${myCart.cart[i].count}',
style:
TextStyle(fontSize: 18),
),
IconButton(
icon: Icon(
Icons.add,
color: Theme.of(context)
.primaryColor,
),
onPressed: () {
Provider.of<Cart>(context,
listen: false)
.addItemToCart(
item: myCart
.cart[i]);
},
),
],
),
),
);
},
separatorBuilder:
(BuildContext context, int) {
return Divider(
height: 2,
color: Colors.grey,
);
},
itemCount: myCart.cart.length,
),
),
SizedBox(height: 20),
Container(
child: TextFormField(
keyboardType: TextInputType.number,
decoration: InputDecoration(
border: OutlineInputBorder(),
hintText: 'Количество гостей',
),
onChanged: (newValue) {
setState(() => guestCount = newValue);
},
validator: (value) {
if (value == null || value.isEmpty) {
return 'Пожалуйста, введите количество гостей!';
}
return null;
},
),
),
SizedBox(height: 20),
Container(
child: TextFormField(
onChanged: (newValue) {
setState(() => comment = newValue);
},
enabled: isCommentEnabled,
decoration: InputDecoration(
border: OutlineInputBorder(),
hintText:
'Лаваш острый, хот дог без майонеза и т.д.',
// labelText: 'Комментарий',
),
maxLines: 2,
),
),
Row(
children: [
Text('Комментарий'),
Switch(
value: isCommentEnabled,
onChanged: (bool value) {
setState(() =>
this.isCommentEnabled = value);
},
),
],
)
],
),
),
SizedBox(height: 20),
Row(
children: [
Text(
'Общая цена: ',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.w500,
),
),
SizedBox(width: 20),
Text(
'${myCart.totalPrice} сум',
style: TextStyle(fontSize: 18),
),
],
),
Container(
height: 50,
width: 170,
child: ElevatedButton(
style: ButtonStyle(
backgroundColor:
MaterialStateProperty.resolveWith(
(states) =>
Theme.of(context).primaryColor),
),
onPressed: () {
if (_formKey.currentState.validate()) {
handleOrderCreate();
}
},
child: Text(
'Отправить',
style: TextStyle(
color: Colors.white, fontSize: 18),
),
),
),
],
),
),
),
),
);
},
),
);
} else {
print('111');
return EmptyCart();
}
},
),
);
}
}
Is there something wrong with my code?