SpeedDial: change color on click - flutter

I'm trying to do so that if I click on a SpeedDialChild (button) then the color of it changes, but even if the value of fun.switchTakePhoto changes from true to false (and vice versa) the color remains red for some reason.
class MainPage extends StatefulWidget {
Home createState() => Home();
}
#immutable
class Home extends State<MainPage> {
//Home({super.key});
var fun = FunctionManager();
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: ElevatedButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => ChangePWD()),
);
},
child: const Text('Change password'),
),
),
floatingActionButton: SpeedDial(
icon: Icons.settings,
backgroundColor: Colors.amber,
children: [
SpeedDialChild(
child: const Icon(Icons.photo_camera),
label: 'Activate/Deactivate: Take Photo',
backgroundColor: fun.switchSendPhoto == true
? const Color.fromARGB(255, 109, 255, 64)
: const Color.fromARGB(255, 255, 64, 64),
onTap: () {
setState(() {
fun.switchTakePhoto = !fun.switchTakePhoto;
});
},
),
]),
);
}
}
class FunctionManager {
bool switchTakePhoto = true;
bool switchSendPhoto = false;
bool switchRec = true;
bool switchPlay = true;
bool switchNotifications = true;
}

The issue might be with data class. I've solve this way.
class FunctionManager {
final bool switchSendPhoto;
FunctionManager({
this.switchSendPhoto = false,
});
FunctionManager copyWith({
bool? switchTakePhoto,
}) {
return FunctionManager(
switchSendPhoto: switchTakePhoto ?? switchSendPhoto,
);
}
}
A state-level variaable like
FunctionManager fun = FunctionManager()
And update data
onTap: () {
fun = fun.copyWith(switchTakePhoto: !fun.switchSendPhoto);
setState(() {});
},
Same goes for others

Related

Change card color based on alertdialog option

I have a list of cards and each card has a long press function which when clicked, pops up an alert dialog. I would like the card to change color based on the option chosen in the alert dialog. My alert dialog has 3 options:
Completed (Card should change to color green),
In Progress ( Color orange),
Cancel (Color grey).
At first, when the screen loads, it should show list of cards each painted the color based on the value saved in the database. Then, when the user long presses a card and chooses an option from the alert dialog, the card's color should change based on the chosen option. Only that particular card's color should change.
I have read somewhere that this might be achievable using valuechangenotifier. So here's what I did so far:
First I created my changenotifier class like below:
import 'package:flutter/material.dart';
class ColorChanger with ChangeNotifier{
Color _color = Colors.white;
ColorChanger(this._color);
getColor() => _color;
setTheme (Color color) {
_color = color;
notifyListeners();
}
}
Then I used it in my dart class. However, the color does not seem to change. What am I missing here?
class OrderItem extends StatefulWidget {
final ord.OrderItem order;
OrderItem(this.order);
#override
_OrderItemState createState() => _OrderItemState();
}
class _OrderItemState extends State<OrderItem> {
var _expanded = false;
var mycolor = Colors.white;
#override
Widget build(BuildContext context) {
ColorChanger _color = Provider.of<ColorChanger>(context);
var listProducts = widget.order.products;
return Card(
color: widget.order.orderStatus=='completed'
?Colors.lightGreen:widget.order.orderStatus=='inprogress'?
Colors.orangeAccent:
widget.order.orderStatus=='cancelled'?Colors.grey:mycolor,
margin: EdgeInsets.all(10),
child: Column(
children: <Widget>[
ListTile(
title: RichText(
text: new TextSpan(
style: new TextStyle(
fontSize: 14.0,
color: Colors.black,
),
children: <TextSpan>[
new TextSpan(
text: 'Order Number : ',
style: new TextStyle(fontWeight: FontWeight.bold)),
new TextSpan(text: widget.order.uniqueOrderNumber),
],
),
),
trailing: IconButton(
icon: Icon(_expanded ? Icons.expand_less : Icons.expand_more),
onPressed: () {
setState(() {
_expanded = !_expanded;
});
},
),
onLongPress: toggleSelection,
),
],
),
);
}
void toggleSelection() {
ColorChanger _color = Provider.of<ColorChanger>(context,listen:false);
Widget completeOrder = FlatButton(
child: Text('Completed'),
onPressed: () async {
try {
Navigator.of(context).pop(true);
// setState(() {
_color.setTheme(Colors.lightGreen);
// });
await Provider.of<Orders>(context, listen: false)
.updateOrder(widget.order,'completed');
} catch (error) {
}
});
Widget startOrder = FlatButton(
child: Text('In progress'),
onPressed: () async {
try {
Navigator.of(context).pop(true);
// setState(() {
_color.setTheme(Colors.orangeAccent);
//});
//Update Db to mark order in progress
await Provider.of<Orders>(context, listen: false)
.updateOrder(widget.order,'inprogress');
} catch (error) {
}
});
Widget cancelOrder = FlatButton(
child: Text('Cancel'),
onPressed: () async {
try {
Navigator.of(context).pop(false);
// setState(() {
_color.setTheme(Colors.grey);
// });
//Update Db to mark order as cancelled
await Provider.of<Orders>(context, listen: false)
.updateOrder(widget.order,'cancelled');
} catch (error) {
}
});
showDialog(
context: context,
builder: (ctx) => AlertDialog(
title: Text('Take Action'),
content: Text('What do you want to do with the order?'),
actions: <Widget>[
startOrder,
completeOrder,
cancelOrder
],
),
);
});
}
}
SECOND TRY based on Loren's answer.
import 'package:flutter/material.dart';
class ColorChanger with ChangeNotifier{
Color color = Colors.white;
setTheme (Color newColor) {
color = newColor;
notifyListeners();
}
}
class OrderItem extends StatefulWidget {
final ord.OrderItem order;
OrderItem(this.order);
#override
_OrderItemState createState() => _OrderItemState();
}
class _OrderItemState extends State<OrderItem> {
var _expanded = false;
//Set the color based on what was last saved in the DB
void didChangeDependencies() async {
var colorChanger = Provider.of<ColorChanger>(context, listen: false);
if(widget.order.orderStatus=='completed')
colorChanger.setTheme(Colors.lightGreen);
else if(widget.order.orderStatus=='inprogress')
colorChanger.setTheme(Colors.orangeAccent);
else if(widget.order.orderStatus=='cancelled')
colorChanger.setTheme(Colors.grey);
super.didChangeDependencies();
}
#override
Widget build(BuildContext context) {
var listProducts = widget.order.products;
return Consumer<ColorChanger>(
builder: (context, colorChanger, child) {
return Card(
color: widget.order.orderStatus=='completed'
?Colors.lightGreen:widget.order.orderStatus=='inprogress'?
Colors.orangeAccent:
widget.order.orderStatus=='cancelled'?Colors.grey:mycolor,
margin: EdgeInsets.all(10),
child: Column(
children: <Widget>[
ListTile(
title: RichText(
text: new TextSpan(
style: new TextStyle(
fontSize: 14.0,
color: Colors.black,
),
children: <TextSpan>[
new TextSpan(
text: 'Order Number : ',
style: new TextStyle(fontWeight: FontWeight.bold)),
new TextSpan(text: widget.order.uniqueOrderNumber),
],
),
),
trailing: IconButton(
icon: Icon(_expanded ? Icons.expand_less : Icons.expand_more),
onPressed: () {
setState(() {
_expanded = !_expanded;
});
},
),
onLongPress: toggleSelection,
),
],
),
)};
}
void toggleSelection() {
ColorChanger _color = Provider.of<ColorChanger>(context,listen:false);
Widget completeOrder = FlatButton(
child: Text('Completed'),
onPressed: () async {
try {
Navigator.of(context).pop(true);
// setState(() {
_color.setTheme(Colors.lightGreen);
// });
await Provider.of<Orders>(context, listen: false)
.updateOrder(widget.order,'completed');
} catch (error) {
}
});
Widget startOrder = FlatButton(
child: Text('In progress'),
onPressed: () async {
try {
Navigator.of(context).pop(true);
// setState(() {
_color.setTheme(Colors.orangeAccent);
//});
//Update Db to mark order in progress
await Provider.of<Orders>(context, listen: false)
.updateOrder(widget.order,'inprogress');
} catch (error) {
}
});
Widget cancelOrder = FlatButton(
child: Text('Cancel'),
onPressed: () async {
try {
Navigator.of(context).pop(false);
// setState(() {
_color.setTheme(Colors.grey);
// });
//Update Db to mark order as cancelled
await Provider.of<Orders>(context, listen: false)
.updateOrder(widget.order,'cancelled');
} catch (error) {
}
});
showDialog(
context: context,
builder: (ctx) => AlertDialog(
title: Text('Take Action'),
content: Text('What do you want to do with the order?'),
actions: <Widget>[
startOrder,
completeOrder,
cancelOrder
],
),
);
});
}
}
When I do it this way, it changes the color of all the cards instead of just that one card. What am I doing wrong here?
Sharing order.dart
class OrderItem {
final String id;
final double amount;
final int deliveryFee;
final List<CartItem> products;
final DateTime dateTime;
final String deliveryMethod;
final String uniqueOrderNumber;
final String orderStatus;
final String userId;
final String customMessage;
final String customerName;
final String phoneNumber;
OrderItem(
{#required this.id,
#required this.amount,
#required this.products,
#required this.dateTime,
#required this.deliveryMethod,
#required this.uniqueOrderNumber,
#required this.isOrderComplete,
this.orderStatus,
#required this.customMessage,
#required this.deliveryFee,
this.customerName,
this.phoneNumber,
#required this.userId});
}
class Orders with ChangeNotifier {
final String authToken;
final String userId;
Orders(this.authToken, this.userId);
List<OrderItem> _orders = [];
List<OrderItem> get orders {
return [..._orders];
}
Future<void> updateOrder(OrderItem order,String orderStatus) async {
final id = order.id;
final customerId = order.userId;
final url =
'https://cv.firebaseio.com/orders/$customerId/$id.json?auth=$authToken';
try {
await http.patch(url,
body: json.encode({
'orderStatus':orderStatus
}));
} catch (error) {
print(error);
}
notifyListeners();
}
UPDATED ANSWER:
So when trying to do this with Provider I kept getting errors that would have required me to keep bugging you for more and more code to try and replicate everything you have going on, and I didn't want to get into that.
So this solution may or may not be acceptable to you because it uses GetX State Management, but it works. In addition it doesn't require wrapping your whole app in provider widgets so dealing with scope etc...is a non issue.
Let's add a statusColor property to your OrderItem model. This is what will get changed.
Color statusColor = Colors.white; // or whatever you you want the default color to be
Your updated Orders class that uses GetX instead of ChangeNotifier (again, not because Provider can't do this, but because I was dealing with too many errors and frankly GetX is easier in my opinion anyway)
class Orders extends GetxController {
final String authToken;
final String userId;
Orders(this.authToken, this.userId);
List<OrderItem> orders = []; // back to what I said earlier about no point in getters and setters here
// temp function just to test this on my end
void addOrder(OrderItem order) {
orders.add(order);
update();
}
// this loops through the list to find the matching order number,
// then updates the color for just that order
void updateOrderStatusColor({OrderItem updatedOrder, String status}) {
for (final order in orders) {
if (order.uniqueOrderNumber == updatedOrder.uniqueOrderNumber) {
switch (status) {
case 'completed':
{
order.statusColor = Colors.greenAccent;
}
break;
case 'inprogress':
{
order.statusColor = Colors.orangeAccent;
}
break;
case 'cancelled':
{
order.statusColor = Colors.grey;
}
break;
}
}
}
update(); // equivelent of notifyListeners();
}
// ...the rest of your class
}
A few small changes to your card. didChangeDependencies can go away entirely.
// it seems like you had 2 classes with the same name, which is not recommended
class OrderItemCard extends StatefulWidget {
final OrderItem order;
OrderItemCard(this.order);
#override
_OrderItemCardState createState() => _OrderItemCardState();
}
class _OrderItemCardState extends State<OrderItemCard> {
var _expanded = false;
final controller = Get.find<Orders>(); // equivilent of Provider.of... finds the same instance without needing context
void toggleSelection() {
Widget completeOrder = TextButton(
child: Text('Completed'),
onPressed: () async {
try {
Navigator.of(context).pop(true);
controller.updateOrderStatusColor(
updatedOrder: widget.order, status: 'completed'); // calling new function here
} catch (error) {}
});
Widget startOrder = FlatButton(
child: Text('In progress'),
onPressed: () async {
try {
Navigator.of(context).pop(true);
controller.updateOrderStatusColor(
updatedOrder: widget.order, status: 'inprogress');
} catch (error) {}
});
Widget cancelOrder = FlatButton(
child: Text('Cancel'),
onPressed: () async {
controller.updateOrderStatusColor(
updatedOrder: widget.order, status: 'cancelled');
try {
Navigator.of(context).pop(false);
} catch (error) {}
});
showDialog(
context: context,
builder: (ctx) => AlertDialog(
title: Text('Take Action'),
content: Text('What do you want to do with the order?'),
actions: <Widget>[startOrder, completeOrder, cancelOrder],
),
);
}
#override
Widget build(BuildContext context) {
return Card(
margin: EdgeInsets.all(10),
color: widget.order.statusColor, // new color property added to your model
child: Column(
children: <Widget>[
ListTile(
title: RichText(
text: new TextSpan(
style: new TextStyle(
fontSize: 14.0,
color: Colors.black,
),
children: <TextSpan>[
new TextSpan(
text: 'Order Number : ${widget.order.uniqueOrderNumber} ',
style: new TextStyle(fontWeight: FontWeight.bold)),
],
),
),
trailing: IconButton(
icon: Icon(_expanded ? Icons.expand_less : Icons.expand_more),
onPressed: () {
setState(() {
_expanded = !_expanded;
});
},
),
onLongPress: toggleSelection,
),
],
),
);
}
}
Not sure what you have going on in your UI but here's a quick demo of how it would work in GetX. It's a simple ListView.builder populated from the orders list from the GetX Class. The GetBuilder<Orders> widget rebuilds when update() is called. Also a simple button that adds a dummy item for demo purposes. I don't know how you're generating your unique order # but I'm just using the list index for this. Both inside a column within a scaffold on a demo page.
// Equivilent of Consumer but doesn't need context nor any provider widget above it
GetBuilder<Orders>(
builder: (controller) => Expanded(
child: ListView.builder(
itemCount: controller.orders.length,
itemBuilder: (context, index) =>
OrderItemCard(controller.orders[index])),
),
),
TextButton(
onPressed: () {
final controller = Get.find<Orders>();
final orderItem = OrderItem(
orderStatus: ' ',
uniqueOrderNumber: controller.orders.length
.toString(), // just a hack to generate a unique order # for demo
);
controller.addOrder(orderItem);
},
child: Text('Add Item'),
)
Last thing is just initializing the GetX Controller. It can be done anywhere as long as its before you try and use it.
void main() {
// initialing the GetX GetxController
// not sure how you're generating the required auth and user id
// but I'm just passing in empty strings for now
Get.put(Orders('', ''));
runApp(MyApp());
}
So if you're open to GetX here, you can leave Provider for any other ChangeNotifier classes you may have in place if you want. For this you would just need to replace any Consumer<Orders> with GetBuilder<Order> and then get rid of the Provider<Orders>(create:... widget entirely.
OLD ANSWER:
You're missing a couple things in order to be using Provider properly and get the color changing the way you want.
For starters, your Card needs to be wrapped in a Consumer widget that gets notified of changes and rebuilds its children. Inside the Consumer, you need to be using the color property of the ChangeNotifier class. It doesn't need to know or care about the orderStatus because you're already explicitly telling it to change color when you call the setTheme method.
Consumer<ColorChanger>( // this is what rebuilds and changes the color
builder: (context, colorChanger, child) {
return Card(
color: colorChanger.color, // colorChanger here is equivalent of declaring final colorChanger = Provider.of<ColorChanger>(context...
child: Column(
children: <Widget>[
ListTile(
title: RichText(
text: new TextSpan(
style: new TextStyle(
fontSize: 14.0,
color: Colors.black,
),
children: <TextSpan>[
new TextSpan(
text: 'Order Number : ',
style: new TextStyle(fontWeight: FontWeight.bold)),
new TextSpan(text: widget.order.uniqueOrderNumber),
],
),
),
trailing: IconButton(
icon: Icon(_expanded ? Icons.expand_less : Icons.expand_more),
onPressed: () {
setState(() {
_expanded = !_expanded;
});
},
),
onLongPress: toggleSelection,
),
],
),
);
});
Next, see this link as to why you're not gaining anything with using the private _color and public getColor in your ChangeNotifier class.
So lets simplify that a bit.
class ColorChanger with ChangeNotifier {
Color color = Colors.white;
ColorChanger(this.color);
setTheme(Color newColor) {
color = newColor;
notifyListeners();
}
}
Now, whenever you call the setTheme function from your dialog, that card will change to whatever color you pass into it because the Consumer widget is notified, and will rebuild with the updated color value of the ChangeNotifier class.
Something like this would be the simplest way to the thing you want to achieve:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
// define a list of colors:
final colors = <Color>[
Colors.white, // this is the inital color
Colors.green,
Colors.orange,
Colors.grey
];
int index = 0;
Future<int> showMyDialog(BuildContext context) async {
// Since all Navigator.push(...) and showDialog(...) calls are futures
// we can send values alongside them when we pop the context:
// final value = await Navigator.push(...);
// or
// final value = await showDialog(...);
// then we do a:
// Navigator.pop(context, SOME_VALUE,);
// the value variable will be assigned to the one we sent
return await showDialog(
context: context,
builder: (context) => AlertDialog(
title: Text('Take Action'),
content: Text('What do you want to do with the order?'),
actions: <Widget>[
TextButton(
child: Text('Completed',
style: TextStyle(
color: Colors.green,
)),
onPressed: () => Navigator.pop(context, 1)),
TextButton(
child: Text('In progress',
style: TextStyle(
color: Colors.orange,
)),
onPressed: () => Navigator.pop(context, 2)),
TextButton(
child: Text('Cancel',
style: TextStyle(
color: Colors.grey,
)),
onPressed: () => Navigator.pop(context, 3)),
],
),
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(children: <Widget>[
Card(
color: colors[index],
child: Container(width: 50, height: 50),
),
ElevatedButton(
child: Text('Show dialog'),
onPressed: () async {
// call the showMyDialog function, it returns
// a future int so we have to await it
final int _index = await showMyDialog(context);
// if the returned value (_index) is null we use
// the old one value to avoid erros in the code
setState(() => index = _index ?? index);
}),
]),
);
}
}
A very simple workaround would be to declare a global color variable cardColor and assign it to the color property of the card. Then on the alertdialog, change the 'onChange'or 'onTap' property of the widget so that on tapping, the widget changes the value of the global variable cardColor to a different color. Don't forget to do the final step i.e. changing the value of the variable, inside setState()
The best way to achieve it by using AwesomeDialog
https://pub.dev/packages/awesome_dialog
AwesomeDialog(
context: context,
dialogType: DialogType.INFO,
animType: AnimType.BOTTOMSLIDE,
title: 'Dialog Title',
desc: 'Dialog description here.............',
btnCancelOnPress: () {},
btnOkOnPress: () {},
)..show();

Flutter : How to get information about which picture in the grid has been selected?

How to get information about which picture in the grid has been selected in flutter. Please check my code!
I really need an answer to this. Please help me. I am looking forward to hearing from all of you.
Getting image and put it into a grid.
ImageEvalation.dart
class ImageEvaluation extends StatefulWidget {
#override
_ImageEvaluationState createState() => _ImageEvaluationState();
}
class _ImageEvaluationState extends State<ImageEvaluation> {
File _selectedFile;
bool _inProcess = false;
String colorCode = '#33695d';
getImage(ImageSource source, BuildContext context) async {
this.setState(() {
_inProcess = true;
});
// File image = await ImagePicker.pickImage(source: source);
final _picker = ImagePicker();
PickedFile image = await _picker.getImage(source: source);
if (image != null) {
// Remove crop attribute if we don't want to resize the image
File cropped = await ImageCropper.cropImage(
sourcePath: image.path,
aspectRatio: CropAspectRatio(ratioX: 1, ratioY: 1),
compressQuality: 100, // 100 means no compression
maxWidth: 700,
maxHeight: 700,
compressFormat: ImageCompressFormat.jpg,
androidUiSettings: AndroidUiSettings(
toolbarColor: HexColor(colorCode),
toolbarTitle: "RPS Cropper",
statusBarColor: HexColor(colorCode),
backgroundColor: Colors.white,
//toolbarWidgetColor: HexColor(colorCode),
activeControlsWidgetColor: HexColor(colorCode),
//dimmedLayerColor: HexColor(colorCode),
cropFrameColor: HexColor(colorCode),
cropGridColor: HexColor(colorCode),
),
);
this.setState(() {
_selectedFile = cropped;
_inProcess = false;
//_showDelete = true;
});
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => UploadScreen(
image: _selectedFile,
),
),
);
} else {
this.setState(() {
_inProcess = false;
});
}
}
#override
Widget build(BuildContext context) {
return _inProcess
? Loading()
: Scaffold(
body: StreamProvider<List<ImageProperty>>.value(
value: User_DatabaseService().pictureData,
child: SingleChildScrollView(
child: Center(
child: Column(
children: <Widget>[
Text('GridView'),
PictureLinkGrid(),
Text('Image Evulation'),
MaterialButton(
onPressed: () {
getImage(ImageSource.camera, context);
},
color: Colors.deepOrange,
child: Text(
'NEXT',
style: TextStyle(color: Colors.white),
),
),
],
),
),
),
),
);
}
}
Make a grid for my images.
PictureGrid.dart
class PictureLinkGrid extends StatefulWidget {
#override
_PictureLinkGridState createState() => _PictureLinkGridState();
}
class _PictureLinkGridState extends State<PictureLinkGrid> {
#override
Widget build(BuildContext context) {
final pictureData = Provider.of<List<ImageProperty>>(context) ?? [];
final neededPicture = [];
final demoPicture = [];
int count = 0;
// get Demo Picture
pictureData.forEach((picture) {
if (picture.title.contains('demo')) {
demoPicture.add(picture);
}
});
// get Needed Picture
pictureData.forEach((picture) {
if (picture.display_count < 10 && !picture.title.contains('demo')) {
print('${picture.title} is NOT null');
neededPicture.add(picture);
} else {
print('${picture.title} is null');
}
});
// fill in the empty picture
count = 0;
while (neededPicture.length < 9) {
neededPicture.add(demoPicture[count]);
count++;
}
return GridView.builder(
//itemCount: neededPicture.length,
itemCount: neededPicture.length,
shrinkWrap: true,
gridDelegate:
SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 3),
itemBuilder: (BuildContext context, int index) {
print(
'Picture title in picturelink grid: ${neededPicture[index].title}');
return TouchableWebImageCard(imagePath: neededPicture[index].url);
});
}
}
Make ImageCard which can be clicked and unchecked.
TouchableWebImageCard.dart
class TouchableWebImageCard extends StatefulWidget {
String imagePath;
TouchableWebImageCard({#required this.imagePath});
//
#override
_TouchableWebImageCardState createState() =>
_TouchableWebImageCardState(imagePath);
}
class _TouchableWebImageCardState extends State<TouchableWebImageCard> {
// To pass parameters
double width;
double height;
String imagePath;
_TouchableWebImageCardState(this.imagePath);
//
bool isChecked = false;
double sigmaX = 0.0;
double sigmaY = 0.0;
double showBorder = 0;
//
checkIcon() {
return isChecked
? Center(
child: Icon(
Icons.check_circle,
color: Colors.white,
size: 50,
),
)
: Container();
}
#override
Widget build(BuildContext context) {
return Stack(
children: <Widget>[
Center(
child: SpinKitCircle(
color: HexColor('#33695d'),
size: 50,
),
),
Center(
child: InkWell(
child: Stack(
children: <Widget>[
FadeInImage.memoryNetwork(
placeholder: kTransparentImage,
image: imagePath,
),
checkIcon(),
],
),
onTap: () {
print('Image Path: $imagePath');
if (isChecked != true) {
setState(
() {
showBorder = 4.0;
isChecked = !isChecked;
},
);
} else {
setState(
() {
showBorder = 0.0;
isChecked = !isChecked;
},
);
}
},
),
),
],
);
}
}
In your TouchableWebImageCard constructor add another variable int _index;
So will know which image you have selected.
Also, the "right" way to make a constructor is:
class TouchableWebImageCard extends StatefulWidget {
TouchableWebImageCard({#required this.imagePath, this._index});
String imagePath;
int _index;

Flutter: Save state of icons when switching flutter local notifications on/off using shared preferences

I am using the following code to switch flutter local notifications on/off. This code works fine, but the icon state does not get saved when the app is closed and reopen.
I need to get this current selected icon saved using shared preferences plugin, but I could not manage to do that.
Can someone help me to add shared preferences to this code.
This the variable:
var _icon2 = Icons.notifications_off;
This is the code of the icons which run the functions between on/off:
IconButton(
icon: Icon(
_icon2,
color: Colors.blue,
size: 30,
),
onPressed: () {
setState(() {
if (_icon2 == Icons.notifications_off) {
_icon2 = Icons.notifications_active;
_repeatNotification2();
} else {
_icon2 = Icons.notifications_off;
_cancelNotification2();
}
});
},
),
I managed to add shared preferences to the code.
import 'package:shared_preferences/shared_preferences.dart';
import 'package:flutter/material.dart';
class SimpleBooleanScreen extends StatefulWidget {
#override
SimpleBooleanScreenState createState() => SimpleBooleanScreenState();
}
class SimpleBooleanScreenState extends State<SimpleBooleanScreen> {
IconData _FirstIcon = Icons.notifications_active;
bool isIconOneActive = true;
String keyNameOne = "_updateScreenOneState";
Future<bool> loadDataOne() async {
SharedPreferences preferences = await SharedPreferences.getInstance();
return preferences.getBool(keyNameOne) ?? true;
}
Future<bool> saveDataOne() async {
SharedPreferences preferences = await SharedPreferences.getInstance();
return preferences.setBool(keyNameOne, isIconOneActive);
}
setData() async {
loadDataOne().then((value) {
setState(() {
isIconOneActive = value;
setIcon();
});
});
}
setIcon() async {
if (isIconOneActive) {
_FirstIcon = Icons.notifications_active;
} else {
_FirstIcon = Icons.notifications_off;
}
}
#override
void initState() {
setData();
super.initState();
}
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text(''),
),
body: SingleChildScrollView(
scrollDirection: Axis.vertical,
child: Padding(
padding: EdgeInsets.all(8.0),
child: Center(
child: Column(
children: <Widget>[
ListTile(
title: Text('Notificaation 1',
style: TextStyle(fontSize: 26.0)),
trailing: IconButton(
icon: Icon(
_FirstIcon,
size: 40.0,
color: Colors.blue,
),
onPressed: () {
setState(() {
if (isIconOneActive) {
isIconOneActive = false;
setIcon();
saveDataOne();
_cancelNotification1();
} else {
isIconOneActive = true;
setIcon();
saveDataOne();
_repeatNotification1();
}
});
},
),
),
],
),
),
),
),
),
);
}

how to refresh state on Navigator.Pop or Push in flutter

Here I have two pages first is called BSP_signup_terms page and the second is Bsp_Service_page. when I am on BSP_signup_terms on that page I have to select some checkbox based on the selected checkbox it will show me some data. but problem is that it will show me the complete data but when I get back to the BSP_signup_terms from Bsp_signup_page and I am changing the checkbox and then again when I am pressing next button it will not change the result it same as the previous result.
Here is the Image of Output Page
In this image I've attached both screen output when I am selecting only one checkbox it will render some value in service page and when I am back to the Terms and Condition page and select one more checkbox then it will not updating service page
Here is the code I've tried.
BSP_Signup_Terms_Page
class BspLicensedSignupTermsPage extends StatefulWidget {
static const String routeName = "/bspLicensedSignupTerms";
final BspSignupCommonModel bspSignupCommonModel;
BspLicensedSignupTermsPage({
Key key,
#required this.bspSignupCommonModel,
}) : super(key: key);
#override
_BspLicensedSignupTermsPageState createState() =>
_BspLicensedSignupTermsPageState();
}
class _BspLicensedSignupTermsPageState
extends State<BspLicensedSignupTermsPage> {
#override
void initState() {
super.initState();
}
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
bool _isWalkIn = false;
bool _isHome = false;
bool _isOnDemand = false;
Widget _buildselectcheckbox() {
return Text(
AppConstantsValue.appConst['bsplicensedsignupterms']['selectcheck']
['translation'],
);
}
// Walkin
_onCustomerWalkin(value) {
setState(() {
_isWalkIn = value;
});
}
Widget _buildCustomerWalkIn() {
return TudoConditionWidget(
text: AppConstantsValue.appConst['bsplicensedsignupterms']
['CustomerWalkIn']['translation'],
onChanged: (value) {
print(value);
_onCustomerWalkin(value);
},
validate: false,
);
}
// Home
_onCustomerInHome(value) {
setState(() {
_isHome = value;
});
}
Widget _buildCustomerInHome() {
return TudoConditionWidget(
text: AppConstantsValue.appConst['bsplicensedsignupterms']
['CustomerInHome']['translation'],
onChanged: (value) {
_onCustomerInHome(value);
},
validate: false,
);
}
Widget _buildCustomerInHomeHelp() {
return Text(
AppConstantsValue.appConst['bsplicensedsignupterms']['businesscheckhelp']
['translation'],
);
}
// On Demand
_onCustomerOnDemand(value) {
setState(() {
_isOnDemand = value;
});
}
Widget _buildBusinessOnDemand() {
return TudoConditionWidget(
text: AppConstantsValue.appConst['bsplicensedsignupterms']
['BusinessOnDemand']['translation'],
onChanged: (value) {
_onCustomerOnDemand(value);
},
validate: false,
);
}
Widget _buildBusinessOnDemandHelp() {
return Text(AppConstantsValue.appConst['bsplicensedsignupterms']
['businessprovidehelp']['translation']);
}
#override
Widget build(BuildContext context) {
final appBar = AppBar(
title: Text("Bsp Licensed Signup Terms and Condition"),
leading: IconButton(
icon: Icon(Icons.arrow_back_ios),
onPressed: () {
NavigationHelper.navigatetoBack(context);
},
),
centerTitle: true,
);
final bottomNavigationBar = Container(
height: 56,
//margin: EdgeInsets.symmetric(vertical: 24, horizontal: 12),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
new FlatButton.icon(
icon: Icon(Icons.close),
label: Text('Clear'),
color: Colors.redAccent,
textColor: Colors.black,
padding: EdgeInsets.symmetric(vertical: 10, horizontal: 30),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(7),
),
onPressed: () {
_formKey.currentState.reset();
},
),
new FlatButton.icon(
icon: Icon(FontAwesomeIcons.arrowCircleRight),
label: Text('Next'),
color: colorStyles["primary"],
textColor: Colors.white,
padding: EdgeInsets.symmetric(vertical: 10, horizontal: 30),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(7),
),
onPressed: () {
if (_formKey.currentState.validate()) {
if (_isHome == false &&
_isOnDemand == false &&
_isWalkIn == false) {
showDialog(
barrierDismissible: false,
context: context,
builder: (context) => ShowErrorDialog(
title: Text('Select Service'),
content: Text(
'Please select atleast one service type to proceed next',
),
));
} else {
BspSignupCommonModel model = widget.bspSignupCommonModel;
model.isWalkin = _isWalkIn;
model.isHome = _isHome;
model.isOnDemand = _isOnDemand;
print(model.toJson());
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
BspServicePage(bspSignupCommonModel: model),
),
);
}
}
},
),
],
),
);
return new Scaffold(
appBar: appBar,
bottomNavigationBar: bottomNavigationBar,
body: Container(
height: double.infinity,
width: double.infinity,
child: Stack(
children: <Widget>[
SingleChildScrollView(
child: SafeArea(
child: Form(
autovalidate: true,
key: _formKey,
child: Scrollbar(
child: SingleChildScrollView(
dragStartBehavior: DragStartBehavior.down,
padding: const EdgeInsets.symmetric(horizontal: 10.0),
child: new Container(
decoration: BoxDecoration(
borderRadius: new BorderRadius.circular(25)),
child: new Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
_buildselectcheckbox(),
_buildCustomerWalkIn(),
_buildCustomerInHome(),
_buildCustomerInHomeHelp(),
_buildBusinessOnDemand(),
_buildBusinessOnDemandHelp(),
],
),
),
),
),
),
),
),
],
),
),
);
}
}
BSP_Service_Page
class BspServicePage extends StatefulWidget {
static const String routeName = "/bspService";
final BspSignupCommonModel bspSignupCommonModel;
BspServicePage({
Key key,
#required this.bspSignupCommonModel,
}) : super(key: key);
#override
_BspServicePageState createState() => _BspServicePageState();
}
class _BspServicePageState extends State<BspServicePage> {
List<int> servicesIds = [];
Map<String, bool> selection = {};
List<BspServices.Service> selectedServices = [];
SearchBarController _controller = new SearchBarController();
String _searchText = '';
bool refreshservices = true;
#override
void initState() {
super.initState();
}
void _showErrorDialog(String message) {
showDialog(
barrierDismissible: false,
context: context,
builder: (context) => ShowErrorDialog(
title: Text('An Error Occurred!'),
content: Text(message),
),
);
}
void refresh() {
setState(() {
refreshservices = !refreshservices;
});
}
#override
Widget build(BuildContext context) {
var _bspServiceBloc = new BspServiceBloc();
final appBar = SearchBar(
controller: _controller,
onQueryChanged: (String query) {
print('Search Query $query');
setState(() {
_searchText = query;
});
},
defaultBar: AppBar(
centerTitle: true,
leading: IconButton(
icon: Icon(Icons.arrow_back_ios),
onPressed: () {
refresh();
NavigationHelper.navigatetoBack(context);
}),
title: Text('Select Services'),
),
);
final bottomNavigationBar = Container(
height: 56,
// margin: EdgeInsets.symmetric(vertical: 24, horizontal: 12),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
new FlatButton.icon(
icon: Icon(Icons.close),
label: Text('Clear'),
color: Colors.redAccent,
textColor: Colors.black,
padding: EdgeInsets.symmetric(vertical: 10, horizontal: 30),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(7),
),
onPressed: () {
print('reseting the state');
setState(() {
selection = {};
servicesIds = [];
});
},
),
new FlatButton.icon(
icon: Icon(FontAwesomeIcons.arrowCircleRight),
label: Text('Next'),
color: colorStyles["primary"],
textColor: Colors.white,
padding: EdgeInsets.symmetric(vertical: 10, horizontal: 30),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(7),
),
onPressed: () {
BspSignupCommonModel model = widget.bspSignupCommonModel;
model.servicesIds = servicesIds;
model.services = selectedServices;
print('servicesIds at the next button');
print(servicesIds);
print(model.toJson());
if (servicesIds.length == 0) {
_showErrorDialog(
'You need to select at least one service to proceed next!');
} else {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => BusinessProfilePage(
bspSignupCommonModel: model,
),
),
);
}
},
),
],
),
);
return new Scaffold(
appBar: appBar,
bottomNavigationBar: bottomNavigationBar,
body: new BspServiceScreen(
bspServiceBloc: _bspServiceBloc,
bspSignupCommonModel: widget.bspSignupCommonModel,
servicesIds: servicesIds,
selection: selection,
searchQuery: _searchText,
selectedServices: selectedServices,
refresh: refresh,
),
);
}
}
Bsp_service_screen
class BspServiceScreen extends StatefulWidget {
final BspServiceBloc _bspServiceBloc;
final String searchQuery;
final List<int> servicesIds;
final Map<String, bool> selection;
final BspSignupCommonModel bspSignupCommonModel;
final List<BspServices.Service> selectedServices;
final Function refresh;
const BspServiceScreen({
Key key,
#required BspServiceBloc bspServiceBloc,
#required this.bspSignupCommonModel,
#required this.servicesIds,
#required this.selection,
#required this.selectedServices,
#required this.refresh,
this.searchQuery,
}) : _bspServiceBloc = bspServiceBloc,
super(key: key);
#override
BspServiceScreenState createState() {
return new BspServiceScreenState(_bspServiceBloc);
}
}
class BspServiceScreenState extends State<BspServiceScreen> {
final BspServiceBloc _bspServiceBloc;
BspServiceScreenState(this._bspServiceBloc);
// Map<String, bool> _selection = {};
#override
void initState() {
super.initState();
bool isHome = widget.bspSignupCommonModel.isHome;
bool isWalkIn = widget.bspSignupCommonModel.isWalkin;
bool isOnDemand = widget.bspSignupCommonModel.isOnDemand;
this._bspServiceBloc.dispatch(LoadBspServiceEvent(
countryId: 1,
isHome: isHome,
isOnDemand: isOnDemand,
isWalkin: isWalkIn,
));
}
#override
void dispose() {
super.dispose();
}
#override
Widget build(BuildContext context) {
return BlocBuilder<BspServiceBloc, BspServiceState>(
bloc: widget._bspServiceBloc,
builder: (
BuildContext context,
BspServiceState currentState,
) {
if (currentState is UnBspServiceState) {
return Center(child: CircularProgressIndicator());
}
if (currentState is ErrorBspServiceState) {
return new Container(
child: new Center(
child: new Text(currentState.errorMessage ?? 'Error'),
),
);
}
if (currentState is InBspServiceState) {
// print(
// 'in bsp service state, ${currentState.bspServices.servicesByCountry.length}');
if (currentState.bspServices.servicesByCountry.length == 0) {
return Container(
child: Center(
child: Text("No Services available for this combination"),
),
);
} else {
return new Container(
child:
_renderServices(currentState.bspServices.servicesByCountry),
);
}
}
return Container();
},
);
}
List<ServicesByCountry> finalList = new List();
ListView _renderServices(List<ServicesByCountry> lovCountryServices) {
WidgetsBinding.instance.addPostFrameCallback((_) {
if (widget.searchQuery != '') {
finalList.clear();
lovCountryServices.forEach((ServicesByCountry data) {
if (data.name
.toLowerCase()
.contains(widget.searchQuery.toLowerCase())) {
setState(() {
finalList.add(data);
});
} else {
data.services.forEach((ServiceList.Service services) {
if (services.name
.toLowerCase()
.contains(widget.searchQuery.toLowerCase())) {
setState(() {
finalList.add(data);
});
}
});
}
});
} else {
setState(() {
finalList.clear();
finalList.addAll(lovCountryServices);
});
}
});
return ListView.builder(
shrinkWrap: true,
padding: const EdgeInsets.all(8.0),
itemCount: finalList.length,
itemBuilder: (BuildContext context, int index) {
ServicesByCountry item = finalList[index];
List itemsList = item.services;
return ExpansionTile(
title: Text(item.name),
children: List.generate(itemsList.length, (i) {
widget.selection[itemsList[i].name] =
widget.selection[itemsList[i].name] ?? itemsList[i].isSelected;
return CheckboxListTile(
title: Text(itemsList[i].name),
value: widget.selection[itemsList[i].name],
onChanged: (val) {
setState(() {
widget.selection[itemsList[i].name] = val;
if (val) {
widget.servicesIds.add(itemsList[i].id);
List<BspServices.Service> services =
widget.selectedServices.where((service) {
return service.mainCategory == item.name;
}).toList();
SubCategory subService = new SubCategory(
id: itemsList[i].id,
name: itemsList[i].name,
);
List<SubCategory> subCategories = [];
if (services.length == 0) {
subCategories.add(subService);
widget.selectedServices.add(
new BspServices.Service(
mainCategory: item.name,
mainCategoryId: item.id,
subCategory: subCategories,
),
);
} else {
print('services in else');
print(services[0].subCategory);
subCategories = services[0].subCategory;
subCategories.add(subService);
}
} else {
widget.servicesIds.removeWhere((service) {
return service == itemsList[i].id;
});
List<BspServices.Service> services =
widget.selectedServices.where((service) {
return service.mainCategory == item.name;
}).toList();
services[0].subCategory.removeWhere((subService) {
return subService.id == itemsList[i].id;
});
}
});
print('widget.servicesIds after set state');
print(widget.servicesIds);
},
);
}),
);
},
);
}
}
You can use setState() after return to the first page:
Navigator.push(context, MaterialPageRoute(builder: (context) => Page2())).then((value) {
setState(() {
// refresh state
});
});
Please try below code:-
First you add one method async method:-
void redirectToNextScreen() async {
final Route route = MaterialPageRoute(
builder: (context) => BspServicePage(bspSignupCommonModel: model));
final result = await Navigator.push(mContext, route);
try {
if (result != null) {
if (result) {
//Return callback here.
}
}
} catch (e) {
print(e.toString());
}
}
Then Next you can call this method in "BSP_Signup_Terms_Page" on Next button Pressed event.
Second you can add below line in "BspServicePage" screen Next and Cancel Events.
Navigator.pop(mContext, true); //true means refresh back page and false means not refresh.

Minlines or similar in TextField Flutter

import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
class Todo {
String title;
String description;
Todo(this.title, this.description);
}
class TextEditingControllerWorkaroud extends TextEditingController {
TextEditingControllerWorkaroud({String text}) : super(text: text);
void setTextAndPosition(String newText, int caretPosition) {
int offset = caretPosition != null ? caretPosition : newText.length;
value = value.copyWith(
text: newText,
selection: TextSelection.collapsed(offset: offset),
composing: TextRange.empty);
}
}
void main() {
runApp(MaterialApp(
title: 'Passing Data',
home: TodosScreen(
todos: List.generate(
20,
(i) => Todo(
'Todo $i',
'A description of what needs to be done for Todo $i',
),
),
),
));
}
class TodosScreen extends StatelessWidget {
final List<Todo> todos;
TodosScreen({Key key, #required this.todos}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Todos'),
),
body: ListView.builder(
itemCount: todos.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(todos[index].title),
onTap: () async {
Map results = await Navigator.push(
context,
MaterialPageRoute(
builder: (context) => DetailScreen1(todo: todos[index]),
),
);
if (results["new"]!=results["old"] || results["newTitle"]!=results["oldTitle"]){
todos[index].description = results["new"];
todos[index].title = results["oldTitle"];
final snackBar = SnackBar(duration: Duration(milliseconds: 2000),
content: Text('Todo Saved Succesfully'),
action: SnackBarAction(
label: 'Undo',
onPressed: () {
print("go back to old");
todos[index].description = results["old"];
todos[index].title = results["oldTitle"];
},
),
);
// Find the Scaffold in the Widget tree and use it to show a SnackBar!
Scaffold.of(context).hideCurrentSnackBar();
Scaffold.of(context).showSnackBar(snackBar);
}
},
);
},
),
);
}
}
class DetailScreen1 extends StatefulWidget {
final Todo todo;
DetailScreen1({Key key, #required this.todo}) : super(key: key);
#override
DetailScreen1State createState() => DetailScreen1State();
}
class DetailScreen1State extends State<DetailScreen1> {
var descriptionTextContent = "";
var titleTextContent = "";
#override
void initState() {
super.initState();
print("intialized");
descriptionTextContent = widget.todo.description;
titleTextContent = widget.todo.title;
}
#override
Widget build(BuildContext context) {
TextEditingControllerWorkaroud descriptionEditWidgetController =
TextEditingControllerWorkaroud(text: descriptionTextContent);
TextEditingControllerWorkaroud titleEditWidgetController =
TextEditingControllerWorkaroud(text: titleTextContent);
TextField descriptionEditWidget = new TextField(
maxLines: 4,
keyboardType: TextInputType.multiline,
controller: descriptionEditWidgetController,
onChanged: (value) {
handleCurrentText(value, descriptionEditWidgetController);
},
);
TextField titleEditWidget = new TextField(
maxLines: 1,
keyboardType: TextInputType.text,
controller: titleEditWidgetController,
onChanged: (value) {
handleCurrentTitle(value, titleEditWidgetController);
},
);
descriptionEditWidgetController.setTextAndPosition(
descriptionTextContent, descriptionTextContent.length);
titleEditWidgetController.setTextAndPosition(
titleTextContent, titleTextContent.length);
return WillPopScope(
child: Scaffold(
appBar: AppBar(
title: Text(widget.todo.title),
leading: new IconButton(
icon: new Icon(Icons.arrow_back),
onPressed: () {
SystemChannels.textInput.invokeMethod('TextInput.hide');
Navigator.pop(context, {
'new': descriptionTextContent,
"old": widget.todo.description,
"newTitle": titleTextContent,
"oldTitle": widget.todo.title,
},
);
},
),
),
body: Padding(
padding: EdgeInsets.all(16.0), child: Column(children: <Widget>[titleEditWidget, descriptionEditWidget],)),
),
onWillPop: () {
Navigator.pop(context, {
'new': descriptionTextContent,
"old": widget.todo.description,
"newTitle": titleTextContent,
"oldTitle": widget.todo.title,
},
);
},
);
}
handleCurrentText(String value,
TextEditingControllerWorkaroud descriptionEditWidgetController) {
setState(() {
descriptionTextContent = value;
print("value is " + value);
});
}
void handleCurrentTitle(String value, TextEditingControllerWorkaroud titleEditWidgetController) {
setState(() {
titleTextContent = value;
});
}
}
The code above is properly functioning code that can be run directly. I have the problem that is TextField has property maxlines. If its null then it auto adjust as the text size grows/shrinks. And if we set it to constant as soon as we increase textContent it acts like scrollable in the widget. But what I want is something called "minLines" that is We start with default no of lines(like if we set max lines to constant) and then we can adjust the size of TextField if text grows(like if we set max lines to null). Also when the text content is beyond the range under the screen it becomes scrollable.
I would have handled that easily if I would be allowed to change maxLines attribute at runtime. I would have simply set a listener on textChange and managed the limits. But its final so I can edit that as well. What can I do?
In recent versions of Flutter, there is actually minLines parameter of TextField and it acts as you described.
First of all the very first mistake that I think was that there was no use of changing state continuously as the user enters the text.My work around was to change state only when I want maxlines to edit
So I set maxlines to a variable defined in the class and that variable I modify as soon as I wanted it (when text exceeds the no of characters)
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
class Todo {
String title;
String description;
Todo(this.title, this.description);
}
class TextEditingControllerWorkaroud extends TextEditingController {
TextEditingControllerWorkaroud({String text}) : super(text: text);
void setTextAndPosition(String newText, int caretPosition) {
int offset = caretPosition != null ? caretPosition : newText.length;
value = value.copyWith(
text: newText,
selection: TextSelection.collapsed(offset: offset),
composing: TextRange.empty);
}
}
void main() {
runApp(MaterialApp(
title: 'Passing Data',
home: TodoScreen(
todos: List.generate(
5,
(i) => Todo(
'Todo $i',
'A description of what needs to be done for Todo $i',
),
),
),
));
}
class TodoScreenState extends State<TodoScreen> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Todos'),
),
floatingActionButton: new FloatingActionButton(
onPressed: () async {
setState(() {
print("pressed");
Todo newTodo = Todo("todo", "");
widget.todos.insert(widget.todos.length, newTodo);
});
int index = widget.todos.length - 1;
Map results = await Navigator.push(
context,
MaterialPageRoute(
builder: (context) => DetailScreen1(todo: widget.todos[index]),
),
);
if (results["new"] != results["old"] ||
results["newTitle"] != results["oldTitle"]) {
widget.todos[index].description = results["new"];
widget.todos[index].title = results["newTitle"];
final snackBar = SnackBar(
duration: Duration(milliseconds: 2000),
content: Text('Todo Saved Succesfully'),
action: SnackBarAction(
label: 'Undo',
onPressed: () {
setState(() {
widget.todos[index].description = results["old"];
widget.todos[index].title = results["oldTitle"];
});
},
),
);
// Find the Scaffold in the Widget tree and use it to show a SnackBar!
Scaffold.of(context).hideCurrentSnackBar();
Scaffold.of(context).showSnackBar(snackBar);
}
},
child: Icon(Icons.add),
),
body: ListView.builder(
itemCount: widget.todos.length,
itemBuilder: (context, index) {
return Dismissible(
background: Container(color: Colors.green[700]),
key: Key(widget.todos[index].title),
onDismissed: (direction) {
print(direction);
Todo currentTodo = widget.todos[index];
setState(() {
widget.todos.removeAt(index);
});
final snackBar = SnackBar(
duration: Duration(milliseconds: 2000),
content: Text('Todo Deleted Succesfully'),
action: SnackBarAction(
label: 'Undo',
onPressed: () async {
setState(() {
widget.todos.insert(index, currentTodo);
});
},
),
);
// Find the Scaffold in the Widget tree and use it to show a SnackBar!
Scaffold.of(context).hideCurrentSnackBar();
Scaffold.of(context).showSnackBar(snackBar);
},
child: ListTile(
title: Text(widget.todos[index].title),
onTap: () async {
Map results = await Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
DetailScreen1(todo: widget.todos[index]),
),
);
if (results["new"] != results["old"] ||
results["newTitle"] != results["oldTitle"]) {
widget.todos[index].description = results["new"];
widget.todos[index].title = results["newTitle"];
final snackBar = SnackBar(
duration: Duration(milliseconds: 2000),
content: Text('Todo Saved Succesfully'),
action: SnackBarAction(
label: 'Undo',
onPressed: () {
setState(() {
widget.todos[index].description = results["old"];
widget.todos[index].title = results["oldTitle"];
});
},
),
);
// Find the Scaffold in the Widget tree and use it to show a SnackBar!
Scaffold.of(context).hideCurrentSnackBar();
Scaffold.of(context).showSnackBar(snackBar);
}
},
),
);
},
),
);
}
}
class TodoScreen extends StatefulWidget {
final List<Todo> todos;
TodoScreen({Key key, #required this.todos}) : super(key: key);
#override
TodoScreenState createState() => TodoScreenState();
}
class DetailScreen1 extends StatefulWidget {
final Todo todo;
DetailScreen1({Key key, #required this.todo}) : super(key: key);
#override
DetailScreen1State createState() => DetailScreen1State();
}
class DetailScreen1State extends State<DetailScreen1> {
var descriptionTextContent = "";
var titleTextContent = "";
var size = 3;
var currentSize="fixed";
#override
void initState() {
super.initState();
print("intialized");
descriptionTextContent = widget.todo.description;
titleTextContent = widget.todo.title;
if (descriptionTextContent.length>=100){
size=null;
currentSize="variable";
}
}
#override
Widget build(BuildContext context) {
TextEditingControllerWorkaroud descriptionEditWidgetController =
TextEditingControllerWorkaroud(text: descriptionTextContent);
TextEditingControllerWorkaroud titleEditWidgetController =
TextEditingControllerWorkaroud(text: titleTextContent);
TextField descriptionEditWidget = new TextField(
decoration: new InputDecoration(hintText: 'Description'),
maxLines: size,
keyboardType: TextInputType.multiline,
controller: descriptionEditWidgetController,
onChanged: (value) {
handleCurrentText(value, descriptionEditWidgetController);
},
);
TextField titleEditWidget = new TextField(
decoration: new InputDecoration(hintText: 'Title'),
maxLines: 1,
keyboardType: TextInputType.text,
controller: titleEditWidgetController,
onChanged: (value) {
handleCurrentTitle(value, titleEditWidgetController);
},
);
descriptionEditWidgetController.setTextAndPosition(
descriptionTextContent, descriptionTextContent.length);
titleEditWidgetController.setTextAndPosition(
titleTextContent, titleTextContent.length);
return WillPopScope(
child: Scaffold(
appBar: AppBar(
title: Text(widget.todo.title),
leading: new IconButton(
icon: new Icon(Icons.arrow_back),
onPressed: () {
SystemChannels.textInput.invokeMethod('TextInput.hide');
Navigator.pop(
context,
{
'new': descriptionTextContent,
"old": widget.todo.description,
"newTitle": titleTextContent,
"oldTitle": widget.todo.title,
},
);
},
),
),
body: Padding(
padding: EdgeInsets.all(16.0),
child: Column(
children: <Widget>[titleEditWidget, descriptionEditWidget],
)),
),
onWillPop: () {
Navigator.pop(
context,
{
'new': descriptionTextContent,
"old": widget.todo.description,
"newTitle": titleTextContent,
"oldTitle": widget.todo.title,
},
);
},
);
}
handleCurrentText(String value,
TextEditingControllerWorkaroud descriptionEditWidgetController) {
descriptionTextContent = value;
if (descriptionTextContent.length>100 && currentSize=="fixed"){
setState(() {
print("called");
size = null;
currentSize="variable";
});
}
else if (descriptionTextContent.length<=100&&currentSize=="variable")
{
setState(() {
print("called");
size = 3;
currentSize="fixed";
});
}
}
void handleCurrentTitle(
String value, TextEditingControllerWorkaroud titleEditWidgetController) {
titleTextContent = value;
}
}
The function to note is handleTextChange of descripionTextField