Combining Providers Riverpod - flutter

i'm trying to learn Riverpod and i use this example below to combine two providers, but the value of the dropDownButton never change and stay as the first value of the first dropDownItem , i changed the ConsumerWidget to ConsumerStatefulWidget also and nothing change, if there any help please.
final cityProvider = StateProvider<String>((ref) => 'Country one');
final weatherProvider = StateProvider((ref) {
final city = ref.watch(cityProvider);
return city == 'Country one'
? '25'
: city == 'Country two'
? '30'
: '50';
});
class ExampleSeven extends StatelessWidget {
const ExampleSeven({super.key});
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Combining Providers with Firebase'),
),
body: Center(
child: ElevatedButton(
onPressed: () {
Navigator.of(context).push(MaterialPageRoute(
builder: (context) => const SecondScreen(),
));
},
child: const Text('Go to next Page')),
),
);
}
}
class SecondScreen extends ConsumerWidget {
const SecondScreen({super.key});
#override
Widget build(BuildContext context, ref) {
String? city = ref.watch(cityProvider);
final weather = ref.watch(weatherProvider);
return Scaffold(
appBar: AppBar(
title: const Text('Combining Providers Second Screen'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
DropdownButton(
value: city,
items: const [
DropdownMenuItem(
value: 'Country one',
child: Text('Country one'),
),
DropdownMenuItem(
value: 'Country two',
child: Text('Country two'),
),
DropdownMenuItem(
value: 'Country three',
child: Text('Country three'),
),
],
onChanged: (value) {
city = value;
},
),
Text(
weather,
style: const TextStyle(fontSize: 40),
),
],
),
));
}
}
may i need to change the first Class or need to use listen instead of read ?

You're not updating the value of the provider correctly. Change you onChanged method to
onChanged: (value) {
ref.read(cityProvider.notifier).state = value!;
},

Related

A value of type 'Color' can't be assigned to a variable of type 'MaterialColor'

I have an error: A value of type 'Color' can't be assigned to a variable of type 'MaterialColor' in line with code: selectedValue = newValue!.
Please help me solve it. I am attaching the screen code:
class _ColorPickerWidgetState extends State<ColorPickerWidget> {
List<DropdownMenuItem<Color>> get dropdownItems{
List<DropdownMenuItem<Color>> colorItems = [
const DropdownMenuItem(
value: Colors.yellow,
child: Text('Yellow'),
),
const DropdownMenuItem(
value: Colors.red,
child: Text('Red'),
),
];
return colorItems;
}
// default value of select
MaterialColor selectedValue = Colors.yellow;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Choose a color'),
centerTitle: true,
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
DropdownButton<Color>(
value: selectedValue,
items: dropdownItems,
onChanged: (Color? newValue){
setState(() {
selectedValue = newValue!;
});
},
),
ElevatedButton(onPressed: () => _changeColor(selectedValue),
child: const Text('Next'),
),
],
),
),
);
}
}
Change:
MaterialColor selectedValue = Colors.yellow;
To:
Color selectedValue = Colors.yellow;
While MaterialColor extends ColorSwatch which extends Color, you cannot assign a Color (which might not be a MaterialColor) to MaterialColor
There are 2 two solutions
Maintain only MaterialColor type in all places
class ColorPickerWidget extends StatefulWidget {
const ColorPickerWidget({Key? key}) : super(key: key);
#override
State<ColorPickerWidget> createState() => _ColorPickerWidgetState();
}
class _ColorPickerWidgetState extends State<ColorPickerWidget> {
List<DropdownMenuItem<MaterialColor>> get dropdownItems {
List<DropdownMenuItem<MaterialColor>> colorItems = [
const DropdownMenuItem(
value: Colors.yellow,
child: Text('Yellow'),
),
const DropdownMenuItem(
value: Colors.red,
child: Text('Red'),
),
];
return colorItems;
}
// default value of select
MaterialColor selectedValue = Colors.yellow;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Choose a color'),
centerTitle: true,
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
DropdownButton<MaterialColor>(
value: selectedValue,
items: dropdownItems,
onChanged: (MaterialColor? newValue) {
setState(() {
selectedValue = newValue!;
});
},
),
ElevatedButton(
onPressed: () => _changeColor(selectedValue),
child: const Text('Next'),
),
],
),
),
);
}
}
Maintain only the Color type in all places
class ColorPickerWidget extends StatefulWidget {
const ColorPickerWidget({Key? key}) : super(key: key);
#override
State<ColorPickerWidget> createState() => _ColorPickerWidgetState();
}
class _ColorPickerWidgetState extends State<ColorPickerWidget> {
List<DropdownMenuItem<Color>> get dropdownItems {
List<DropdownMenuItem<Color>> colorItems = [
const DropdownMenuItem(
value: Colors.yellow,
child: Text('Yellow'),
),
const DropdownMenuItem(
value: Colors.red,
child: Text('Red'),
),
];
return colorItems;
}
// default value of select
Color selectedValue = Colors.yellow;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Choose a color'),
centerTitle: true,
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
DropdownButton<Color>(
value: selectedValue,
items: dropdownItems,
onChanged: (Color? newValue) {
setState(() {
selectedValue = newValue!;
});
},
),
ElevatedButton(
onPressed: () => _changeColor(selectedValue),
child: const Text('Next'),
),
],
),
),
);
}
}

How to hide/show widgets using radio buttons in flutter

I have a simple app that I am trying to hide or different widgets when a user clicks on yes or no, but when I select Yes or No all the two widgets are shown instead of only showing one when yes is clicked and No when no is clicked and hide Yes or vice versa,
This is what I have done so far
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
// Hide the debug banner
debugShowCheckedModeBanner: false,
title: 'Show Hide Widgets',
home: HomePage(),
);
}
}
class HomePage extends StatefulWidget {
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
// The inital group value
bool visibilityTag = false;
bool visibilityObs = false;
void _changed(bool visibility, String field) {
setState(() {
if (field == "no"){
visibilityTag = visibility;
}
if (field == "yes"){
visibilityObs = visibility;
}
});
}
String _selectedGender = 'None';
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
centerTitle: true,
title: Text(
'Show Hide Widgets',
),
),
body: Padding(
padding: EdgeInsets.all(25),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('Please pick one:'),
ListTile(
leading: Radio(
value: 'Yes',
groupValue: _selectedGender,
onChanged: (value) {
setState(() {
_selectedGender = value as String;
visibilityObs ? null : _changed(true, "yes");
});
},
),
title: Text('Yes'),
),
ListTile(
leading: Radio(
value: 'no',
groupValue: _selectedGender,
onChanged: (value) {
setState(() {
_selectedItem = value as String;
visibilityTag ? null : _changed(true, "no");
});
},
),
title: Text('No'),
),
SizedBox(height: 25),
visibilityObs ? new Row(
crossAxisAlignment: CrossAxisAlignment.end,
children: <Widget>[
new Expanded(
flex: 11,
child: new TextField(
maxLines: 1,
style: Theme.of(context).textTheme.headline6,
decoration: new InputDecoration(
labelText: "Yes",
isDense: true
),
),
),
new Expanded(
flex: 1,
child: new IconButton(
color: Colors.grey[400],
icon: const Icon(Icons.cancel, size: 22.0,),
onPressed: () {
_changed(false, "yes");
},
),
),
],
) : new Container(),
visibilityTag ? new Row(
crossAxisAlignment: CrossAxisAlignment.end,
children: <Widget>[
new Expanded(
flex: 11,
child: new TextField(
maxLines: 1,
style: Theme.of(context).textTheme.title,
decoration: new InputDecoration(
labelText: "yesno",
isDense: true
),
),
),
new Expanded(
flex: 1,
child: new IconButton(
color: Colors.grey[400],
icon: const Icon(Icons.cancel, size: 22.0,),
onPressed: () {
_changed(false, "no");
},
),
),
],
) : new Container(),
],
)),
);
}
How can I only show the a widget when yes is clicked, and no when no is clicked and Hide the previous yes selected?
From the example in docs:
import 'package:flutter/material.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
static const String _title = 'Flutter Code Sample';
#override
Widget build(BuildContext context) {
return MaterialApp(
title: _title,
home: Scaffold(
appBar: AppBar(title: const Text(_title)),
body: const Center(
child: MyStatefulWidget(),
),
),
);
}
}
enum SingingCharacter { lafayette, jefferson }
class MyStatefulWidget extends StatefulWidget {
const MyStatefulWidget({Key? key}) : super(key: key);
#override
State<MyStatefulWidget> createState() => _MyStatefulWidgetState();
}
class _MyStatefulWidgetState extends State<MyStatefulWidget> {
SingingCharacter? _character = SingingCharacter.lafayette;
#override
Widget build(BuildContext context) {
return Column(
children: <Widget>[
ListTile(
title: const Text('Lafayette'),
leading: Radio<SingingCharacter>(
value: SingingCharacter.lafayette,
groupValue: _character,
onChanged: (SingingCharacter? value) {
setState(() {
_character = value;
});
},
),
),
ListTile(
title: const Text('Thomas Jefferson'),
leading: Radio<SingingCharacter>(
value: SingingCharacter.jefferson,
groupValue: _character,
onChanged: (SingingCharacter? value) {
setState(() {
_character = value;
});
},
),
),
// -------------------------
_character == SingingCharacter.jefferson
? const ListTile(
title: Text('Something goes here!'),
)
: Container(),
// ----------------------
],
);
}
}

Flutter riverpod update state of specific index in ListView

I have an app that makes a post request to an API with data about drivers and orders. The initial page displays a list of drivers in individual list tiles. The list tile has a drop down option. Clicking on that option brings you to a new page with a list view of orders for that driver. Clicking on an individual order brings you to a form. On submitting and validating this form, I want to change the color of that orders text from red to green. Each Order has a submitted flag, and when it submits I would want to change that to true and then have the color change. When all the orders are green within an List View, I want the color of that driver to turn green. I've been going over riverpod tutorials and documentation but can't quite figure out how to get this done. Can someone point me in the right direction?
main.dart
void main() {
runApp(ProviderScope(child: MyApp()));
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
debugShowCheckedModeBanner: false,
home: DriversPage(),
);
}
}
drivers.dart - This is where the drivers are displayed
class DriversPage extends StatelessWidget {
final HttpService httpService = HttpService();
var colorChoice = Colors.red;
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Color(0xFFFAFAFA),
appBar: AppBar(
title: Text("Drivers")
),
body: Container(
child: FutureBuilder(
future: httpService.getOrders(),
builder: (BuildContext context, AsyncSnapshot<List<Order>> snapshot) {
if (snapshot.hasData) {
List<Order> orders = snapshot.data;
return ListView(
children: orders.map((Order order) => Card(child: ExpansionTile(
title: Text(order.driver, style: TextStyle(color: colorChoice),),
children: <Widget>[
Container(
alignment: Alignment.center,
margin: EdgeInsets.all(10.0),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(2.0),
border: Border.all(color: Colors.black26)
),
child: ListTile(title: Text("Orders"),
trailing: Icon(Icons.keyboard_arrow_right),
onTap: () => Navigator.of(context).push(MaterialPageRoute(builder: (context) => OrdersState(driverName: order.driver, driverOrders: order.orders))),),
),
],
))).toList(),
);
}
return Center(child: CircularProgressIndicator());
}),
));
}
}
orders.dart - This is where the orders for a driver are displayed. I originally had it as a stateful widget but turned it into a Consumer Widget and took an attempt at making a provider but was lost on how to handle it in a listview like this. As you can see here I am using the ternary operator for the text color based on item.submitted
final driverListProvider = StateNotifierProvider((ref) => new DriverListTest());
class OrdersState extends ConsumerWidget {
final String driverName;
final List<OrderElement> driverOrders;
const OrdersState({Key key, this.driverName, this.driverOrders}) : super(key: key);
#override
Widget build(BuildContext context, ScopedReader watch) {
return Scaffold(
appBar: AppBar(
title: Text(driverName),
),
body: ListView.builder(
itemCount: driverOrders.length,
itemBuilder: (context, index){
final item = driverOrders[index];
return Card(
key: UniqueKey(),
child: ListTile(title: Text(item.order, style: TextStyle(color: item.submitted? Colors.green : Colors.red),),
subtitle: Text('${item.company}\n${item.address}'),
onTap: () => Navigator.of(context).push(MaterialPageRoute(builder: (context) => OrderForm(orderTitle: item.order,))),));
}),
);
}
}
orderform.dart - Only showing one field for the form, figured the rest was not neccessary, just need to show what happens on submit.
class OrderForm extends StatefulWidget {
final String orderTitle;
const OrderForm({this.orderTitle});
#override
_OrderFormState createState() => _OrderFormState();
}
class _OrderFormState extends State<OrderForm> {
#override
final _formKey = GlobalKey<FormState>();
final _orderModel = Order();
List<String> _pickerNames = ['Loader 1', 'Loader 2', 'Loader 3', 'Loader 4'];
String _selectedPickedBy;
String _selectedCheckedBy;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(backgroundColor: Colors.blueGrey,title: Center(
child: Text(widget.orderTitle),
),),
floatingActionButton: FloatingActionButton(
child: Icon(
Icons.delete
),
onPressed: () {
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
scrollable: true,
title: Text('Login'),
content: Padding(
padding: const EdgeInsets.all(8.0),
child: Form(
child: Column(
children: <Widget>[
TextFormField(
decoration: InputDecoration(
labelText: 'Reason',
icon: Icon(Icons.account_box),
),
),
TextFormField(
decoration: InputDecoration(
labelText: 'Reason 1',
icon: Icon(Icons.email),
),
),
TextFormField(
decoration: InputDecoration(
labelText: 'Reason 2',
icon: Icon(Icons.message),
),
),
],
),
),
),
actions: [
RaisedButton(
child: Text("Submit"),
onPressed: () {
})
],
);
});
}
),
body: Container(
padding: const EdgeInsets.symmetric(vertical: 16.0, horizontal: 16.0),
child: Builder(
builder: (context) => Form(
key: _formKey,
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
DropdownButtonFormField(
isExpanded: true,
hint: Text('Picked By'),
value: _selectedPickedBy,
onChanged: (newValue){
setState(() {
_selectedPickedBy = newValue;
});
},
validator: (value) => value == null
? 'Picked By Required' : null,
items: _pickerNames.map((picker) {
return DropdownMenuItem(
child: new Text(picker),
value: picker,
);
}).toList(),
onSaved: (value) => setState(() => _orderModel.pickedBy = value) ,
),
Container(
padding: const EdgeInsets.symmetric(
vertical: 16.0, horizontal: 16.0
),
child: RaisedButton(
onPressed: (){
final form = _formKey.currentState;
if (form.validate()){
form.save();
Navigator.pop(context,);
}
},
child: Text("Submit"),
),
)
],
)),
),
)
);
}
}
ordermodel.dart - This is the model for the drivers and orders when making http requests to my api. At the bottom you can see where I attempt at making a statenotifier and what I'm trying to with accepting a list of OrderElement(The list of orders).
List<Order> orderFromJson(String str) => List<Order>.from(json.decode(str).map((x) => Order.fromJson(x)));
String orderToJson(List<Order> data) => json.encode(List<dynamic>.from(data.map((x) => x.toJson())));
class Order {
Order({
this.driver,
this.orders,
});
String driver;
List<OrderElement> orders;
factory Order.fromJson(Map<String, dynamic> json) => Order(
driver: json["Driver"],
orders: List<OrderElement>.from(json["Orders"].map((x) => OrderElement.fromJson(x))),
);
Map<String, dynamic> toJson() => {
"Driver": driver,
"Orders": List<dynamic>.from(orders.map((x) => x.toJson())),
};
}
class OrderElement {
OrderElement({
this.order,
this.company,
this.address,
this.submitted,
this.index,
});
String order;
String company;
String address;
bool submitted;
num index;
factory OrderElement.fromJson(Map<String, dynamic> json) => OrderElement(
order: json["Order"],
company: json["Company"],
address: json["Address"],
submitted: json["submitted"],
index: json["index"]
);
Map<String, dynamic> toJson() => {
"Order": order,
"Company": company,
"Address": address,
};
}
class DriverListTest extends StateNotifier<List<OrderElement>> {
DriverListTest([List<OrderElement> drivers1]) : super(drivers1 ?? []);
void onSubmit(num index) {
state = [
for(final currentOrder in state)
if (currentOrder.index == index)
OrderElement(
order: currentOrder.order,
company: currentOrder.company,
address: currentOrder.address,
submitted: !currentOrder.submitted,
index: currentOrder.index,
)
else
currentOrder,
];
}
}
Don't know if my Http class is necessary but let me know if it is. I tried following https://www.refactord.com/guides/riverpod-state-management-explained and How to set the state of a widget at an index in a listview.builder in flutter how to handle individual widgets but again I just got lost. Any help would be greatly appreciated! Thanks in advance.

Flutter: how to use DropDownButton?

i'm trying to build a DropdownButton widget of multiple elements, but I'm miserably failing even if I read multiple tutorials on the Internet.
How can I go about creating a simple DropdownButton of 4 elements ?
Thanks for your time
Here's what I tried:
import 'package:flutter/material.dart';
class ForgotPassScreen extends StatefulWidget {
#override
_ForgotPassScreenState createState() => _ForgotPassScreenState();
}
class _ForgotPassScreenState extends State<ForgotPassScreen> {
int _value = 0;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Dropdown Button"),
),
body: Container(
padding: EdgeInsets.all(20.0),
child: DropdownButton(
value: _value,
items: [
DropdownMenuItem(
child: Text("Item 0"),
value: 0,
),
DropdownMenuItem(
child: Text("First Item"),
value: 1,
),
DropdownMenuItem(
child: Text("Second Item"),
value: 2,
),
DropdownMenuItem(
child: Text("Third Item"),
value: 3,
),
DropdownMenuItem(
child: Text("Fourth Item"),
value: 4,
)
],
onChanged: (value) {
setState(() {
_value = value;
});
}),
));
}
}
So this code has basically 3 parts to it. First is the object which stores the icon and the title. The second is the list of these objects, you can have as many as you want. And third is the button itself which constructs the boxes
OBJECT
class Choice {
const Choice({this.title, this.icon});
final String title;
final IconData icon;
}
LIST
List<Choice> choices = <Choice>[
const Choice(title: 'Profile', icon: Icons.account_circle),
const Choice(title:"Log in", icon: Icons.exit_to_app),
]
POPUP BUTTON
PopupMenuButton<Choice>(
color:Colors.white,
onSelected: onItemMenuPress,
itemBuilder: (BuildContext context) {
return choices.map((Choice choice) {
return PopupMenuItem<Choice>(
value: choice,
child: Row(
children: <Widget>[
Icon(
choice.icon,
color:Colors.black
),
Container(
width: 10.0,
),
Text(
choice.title,
style: TextStyle(),
),
],
));
}).toList();
},
)
This is the best way to create the button as you can modify it without having to change every single piece of code

Flutter: Passing Data between Classes Resets the Other Value

I try to pass sRegions value from Regions class to the AddAd class
and passing sCategory value from Categories class to AddAd class
but when I click and pass this value , it resets the other value to initial value
I want to the values inside AddAd class to be separated and do not affect each other
Here is AddAd() class:
class AddAd extends StatefulWidget {
final String sRegion;
final String sCategory;
AddAd(this.sRegion,this.sCategory, {Key key}):super(key: key);
#override
_AddAdState createState() => _AddAdState();
}
ListTile(
title: Text("Location"),
subtitle: widget.sRegion == null ? Text("Where exactly") : Text(widget.sRegion),
),
ListTile(
title: Text("Category"),
subtitle: widget.sCategory == null ? Text("Department") : Text(widget.sCategory),
),
and here is how I pass data from Regions class:
child: Text(item),
onPressed: () {
sRegion = item;
Navigator.push(context, new MaterialPageRoute(builder: (context) => AddAd(sRegion, sCategory)));
print(sRegion);
},
and here how I pass data from Categories class:
class _RegionsState extends State<Regions> {
String sRegion;
String sCategory;
#override
Widget build(BuildContext context) {
return Directionality(
textDirection: TextDirection.rtl,
child: Scaffold(
appBar: AppBar(
title: Text("Location"),
actions: <Widget>[
FlatButton(
child: Text('Cancel', style: TextStyle(color: Colors.white),),
onPressed: (){
Navigator.pushReplacement(context, MaterialPageRoute(builder: (context)=>AddAd(sRegion, sCategory)));
},
)
],
),
body: SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.only(top:15.0),
child: Container(
child: Column(
children: <Widget>[
for(var item in regions) FlatButton(
child: Text(item), onPressed: (){
sRegion = item;
Navigator.push(context, new MaterialPageRoute(builder: (context) => AddAd(sRegion, sCategory)));
print(sRegion);
},),
Divider(),
],
),
),
),
),
),
);
}
}