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!;
},
i'm pretty much new at flutter , and recently i started to work with PopupMenuButton which i'm using to allow the user to select which language they wish to use and then renders the selected language in a container (using it as it to make it look like a textfield dropdown menu) ,
so basically when the user selects the language the selectedlanguage value actually do change and the app's language also changes but the text inside the container never changes
import 'package:Terzi/main.dart';
import 'package:flutter/material.dart';
class Testable extends StatefulWidget {
#override
_TestableState createState() => _TestableState();
}
class _TestableState extends State<Testable> {
#override
Widget build(BuildContext context) {
final TextEditingController _controller = new TextEditingController();
var languages = ['English','اللغة العربية','Türkçe'];
String slectedLanguage="English";
return new Scaffold(
body: new Center(
child: new Container(
child: new Column(
children: [
new Padding(
padding: const EdgeInsets.all(24.0),
child: new Row(
children: <Widget>[
new Expanded(
child: Container(
decoration: BoxDecoration(
color: Colors.blue
),
height: 65, width: double.infinity,
alignment: Alignment.center,
child: Text(slectedLanguage,style: TextStyle(color: Colors.white,fontSize: 16),),
),
),
new PopupMenuButton<String>(
icon: const Icon(Icons.arrow_drop_down),
onSelected: (String value) {
setState(() {
slectedLanguage= value;
switch( slectedLanguage)
{
case "English" :
MyApp.setLocale(context, Locale('en','US'));
break;
case "اللغة العربية" :
MyApp.setLocale(context, Locale('ar','SA'));
break;
case "Türkçe" :
MyApp.setLocale(context, Locale('tr','TR'));
break;
}
print(slectedLanguage);
});
},
itemBuilder: (BuildContext context) {
return languages.map<PopupMenuItem<String>>((String value) {
return new PopupMenuItem(child: new Text(value), value: value);
}).toList();
},
),
],
),
),
],
),
),
),
);
}
}
Your selectedLanguage will always be 'English' because it is a local variable initialized at the beginning of your build method. Instead, initialize selectedLanguage as an instance variable of your State class _TestableState:
class Testable extends StatefulWidget {
#override
_TestableState createState() => _TestableState();
}
class _TestableState extends State<Testable> {
String selectedLanguage = 'English';
#override
Widget build(BuildContext context) {
final TextEditingController _controller = new TextEditingController();
var languages = ['English', 'اللغة العربية', 'Türkçe'];
return new Scaffold(
body: new Center(
child: new Container(
child: new Column(
children: [
new Padding(
padding: const EdgeInsets.all(24.0),
child: new Row(
children: <Widget>[
new Expanded(
child: Container(
decoration: BoxDecoration(color: Colors.blue),
height: 65,
width: double.infinity,
alignment: Alignment.center,
child: Text(
selectedLanguage,
style: TextStyle(color: Colors.white, fontSize: 16),
),
),
),
new PopupMenuButton<String>(
icon: const Icon(Icons.arrow_drop_down),
onSelected: (String value) {
setState(() {
selectedLanguage = value;
switch (selectedLanguage) {
case "English":
MyApp.setLocale(context, Locale('en', 'US'));
break;
case "اللغة العربية":
MyApp.setLocale(context, Locale('ar', 'SA'));
break;
case "Türkçe":
MyApp.setLocale(context, Locale('tr', 'TR'));
break;
}
print(selectedLanguage);
});
},
itemBuilder: (BuildContext context) {
return languages
.map<PopupMenuItem<String>>((String value) {
return new PopupMenuItem(
child: new Text(value), value: value);
}).toList();
},
),
],
),
),
],
),
),
),
);
}
}
I have an alertDialog containing a DropdownButton widget. Whenever I click on an option within the dropDown I want it to display the selected value. I have listed the code below along with 2 screenshots.
I believe this may be an issue with how flutter builds widget because when I placed the DropdownButton widget outside of the dialog it worked, however placing it within an alertDialog causes it to fail. I also noticed that if I clicked on an option in the DropdownButton and then exited and clicked on the dialog again the selected item would change. However, I want the selected value to change without the user having to tap out of the dialog and then back in.
^
The above image is the dialog when a user first clicks on it. At first the only selected item is "I'm not able to help". This value should change whenever a user clicks on the DropdownMenu widget and selects a different option such as "other".
^
These are the various options a user can click on within the dropdown menu. When a user clicks on it, the menu should update accordingly.
CODE:
Please note that I have defined _chosenValue as a global variable outside of the build function.
void _showDecline() {
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: new Text("Decline Appointment Request"),
content: Container(
height: 100,
width: 200,
child: Column(
children: <Widget>[
new Text("Please select an option for why you declined."),
new DropdownButton<String>(
value: _chosenValue,
underline: Container(),
items: <String>['I\'m not able to help', 'Unclear description', 'Not available at set date and time', 'Other'].map((String value) {
return new DropdownMenuItem<String>(
value: value,
child: new Text(value, style: TextStyle(fontWeight: FontWeight.w500),),
);
}).toList(),
onChanged: (String value) {
setState(() {
_chosenValue = value;
});
},
)
],
),
),
actions: <Widget>[
// usually buttons at the bottom of the dialog
new FlatButton(
child: new Text("Close"),
onPressed: () {},
},
),
],
);
},
);
}
setState will only update current StatefulWidget's Widget Build function.
You should use StatefulBuilder inside showDialog.
For your case just add StatefulBuilder as a parent of your DropDown widget, and use StateSetter when you want to update the StatefulBuilder's children.
It will only update the widget tree defined under StateFulBuilder builder function.
See the full code including stateFulBuilder at DartPad code StateFulBuilderDartPad.
For more information on StatefulBuilder head over to StateFulBuilder documentation page.
import 'dart:convert';
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(home: HomePage());
}
}
class HomePage extends StatefulWidget {
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
String _chosenValue;
void _showDecline() {
showDialog(
context: context,
builder: (BuildContext context) {
return StatefulBuilder(
builder: (BuildContext context, StateSetter setState) {
return AlertDialog(
title: new Text("Decline Appointment Request"),
content:
Column(mainAxisSize: MainAxisSize.min, children: <Widget>[
new Text("Please select an option for why you declined."),
SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: new DropdownButton<String>(
hint: Text('Select one option'),
value: _chosenValue,
underline: Container(),
items: <String>[
'I\'m not able to help',
'Unclear description',
'Not available at set date and time',
'Other'
].map((String value) {
return new DropdownMenuItem<String>(
value: value,
child: new Text(
value,
style: TextStyle(fontWeight: FontWeight.w500),
),
);
}).toList(),
onChanged: (String value) {
setState(() {
_chosenValue = value;
});
},
)),
]),
actions: <Widget>[
// usually buttons at the bottom of the dialog
new FlatButton(
child: new Text("Close"),
onPressed: () {
Navigator.of(context).pop();
},
),
],
);
},
);
},
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Container(
child: FlatButton(child: Text('Click'), onPressed: _showDecline),
),
),
);
}
}
Just check out the below example you have to use the statefulBuilder to change the state.
import 'dart:convert';
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(home: HomePage());
}
}
class HomePage extends StatefulWidget {
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
String _chosenValue;
void _showDecline() {
showDialog(
context: context,
builder: (BuildContext context) {
return StatefulBuilder(
builder: (BuildContext context, StateSetter setState){
return AlertDialog(
title: new Text("Decline Appointment Request"),
content: Container(
height: 100,
width: 200,
child: Column(
children: <Widget>[
new Text("Please select an option for why you declined."),
new DropdownButton<String>(
hint: Text('Select one option'),
value: _chosenValue,
underline: Container(),
items: <String>[
'I\'m not able to help',
'Unclear description',
'Not available at set date and time',
'Other'
].map((String value) {
return new DropdownMenuItem<String>(
value: value,
child: new Text(
value,
style: TextStyle(fontWeight: FontWeight.w500),
),
);
}).toList(),
onChanged: (String value) {
setState(() {
_chosenValue = value;
});
},
)
],
),
),
actions: <Widget>[
// usually buttons at the bottom of the dialog
new FlatButton(
child: new Text("Close"),
onPressed: () {
Navigator.of(context).pop();
},
),
],
);
},
);
},
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Container(
child: FlatButton(child: Text('Click'), onPressed: _showDecline),
),
),
);
}
}
Just let me know if it works.
onTap: () {
///___________________________________________________________________
// Get.defaultDialog(
// title: " وضعیت دزدگیر",
// middleText: "پیام اعلام وضعیت دزدگیر ارسال گردد؟",
// titleStyle: TextStyle(
// color: mainColor2, fontWeight: FontWeight.bold, fontSize: 16),
// middleTextStyle:
// TextStyle(color: mainColor6.withOpacity(0.9), fontSize: 15),
// );
///----------------------------------------------------------------------
// showDialog(
// context: context,
// builder: (context) => AlertDialog(
// content: Column(
// children: <Widget>[
// TextField(
// decoration: InputDecoration(
// icon: Icon(Icons.account_circle),
// labelText: 'Username',
// ),
// ),
// TextField(
// obscureText: true,
// decoration: InputDecoration(
// icon: Icon(Icons.lock),
// labelText: 'Password',
// ),
// ),
// ],
// ),
// ),
// );
///___________________________________________________________________
List<DropdownMenuItem<String>> listDrop = [];
String selected=null;
void loadData() {
listDrop.add(new DropdownMenuItem(
child: new Text("پایدار"),
value:"555",
));
listDrop.add(
new DropdownMenuItem(
child: new Text("لحظه ای"),
value:"444",
),
);
}
loadData();
Alert(
context: context,
title: "تنظیمات خروجی شماره ۱",
// desc: ".",
// image: Image.asset(
// "assets/settings.png",
// scale: 5,
// ),
content: Directionality(
textDirection: TextDirection.rtl,
child: Center(
child: Column(
children: <Widget>[
SizedBox(height: 20.0),
TextField(
keyboardType: TextInputType.text,
controller: _codeShargController,
decoration: InputDecoration(
labelText: 'نام خروجی',
hintText: '${out1.read('codeShargController')}',
),
),
SizedBox(height: 25.0),
Center(
child: DropdownButton(
underline: Container(
height: 1.5,
color: Colors.black26,
),
hint: Text("وضعیت عملکرد"),
items: listDrop,
isExpanded: true,
value: selected,
style: TextStyle(color: Colors.black, fontSize: 16),
onChanged: (newValue) {
selected = newValue;
// setState(() {});
setState(() { selected = newValue; });
},
),
),
SizedBox(height: 25.0),
],
),
),
),
// content: Column(
// children: <Widget>[
//
// SizedBox(height: 10.0),
//
// TextField(
//
// decoration: InputDecoration(
//
// icon: Icon(Icons.account_circle),
// labelText: 'Username',
// ),
// ),
// SizedBox(height: 10.0),
//
// TextField(
// obscureText: true,
// decoration: InputDecoration(
// icon: Icon(Icons.lock),
// labelText: 'Password',
// ),
// ),
// ],
// ),
buttons: [
DialogButton(
onPressed: () {
out1.write(
"codeShargController", _codeShargController.text);
Navigator.pop(context);
},
child: Text(
"ثبت",
style: TextStyle(color: Colors.white, fontSize: 20),
),
)
]).show();
///___________________________________________________________________
Try This ......
implement the alert on separate dart file and call it. that worked for me.
Important - there was used following dropdown plugin because that ui better for me...
Link - dropdown_button2: ^1.2.2
on main page call to the alert as follows.
import 'package:crmapp/pages/payment_history/payment_history_search_dialog.dart';
import 'package:flutter/material.dart';
class PaymentHistoryScreen extends StatefulWidget {
#override
_PaymentHistoryScreenState createState() => _PaymentHistoryScreenState();
}
class _PaymentHistoryScreenState extends State<PaymentHistoryScreen> {
ScrollController scrollController = new ScrollController();
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
// Setting up AppBar
appBar: AppBar(
title: Text('Payment History'),
),
// Body
body: Container(
// your code here - you can use onpressed method in the body also.here I used it for floating button
),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
showDialog(
barrierDismissible: false,
context: context,
builder: (BuildContext context) {
return new PaymentHistorySearchDialog(); //call the alert dart
}
);
},
child: Container
(
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(100)),
),
child: Icon(Icons.search_sharp, size: 32, color: Colors.white,)
)
),
);
}
then code the alert dart as follows.
import 'package:dropdown_button2/dropdown_button2.dart';
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
class PaymentHistorySearchDialog extends StatefulWidget {
#override
State<StatefulWidget> createState() {
return PaymentHistorySearchDialogState();
}
}
class PaymentHistorySearchDialogState extends State<PaymentHistorySearchDialog> {
String? selectedValue;
List<String> items = [
'All',
'Completed',
'Pending',
'Rejected',
];
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
return new AlertDialog(
titlePadding: EdgeInsets.only(top: 20, left: 15, right: 15, bottom: 5),
contentPadding: EdgeInsets.only(
top: 15,
left: 15,
right: 15,
bottom: 5
),
title: Text(
'Search'.toUpperCase(),
style: TextStyle(
color: Colors.black,
fontSize: 22,
fontWeight: FontWeight.w600,
fontFamily: "medium",
)
),
content: Container(
width: double.infinity,
height: 220,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
children: [
DropdownButtonHideUnderline(
child: DropdownButton2(
hint: Text(
'Select Status',
style: TextStyle(
fontSize: 14,
),
),
items: items
.map((item) =>
DropdownMenuItem<String>(
value: item,
child: Text(
item,
style: const TextStyle(
fontSize: 14,
),
),
))
.toList(),
value: selectedValue,
onChanged: (value) {
setState(() {
selectedValue = value as String;
//Navigator.of(context).pop();
});
print(value);
// selectedValue = value as String;
},
buttonHeight: 30,
buttonWidth: double.infinity,
itemHeight: 40,
buttonDecoration: BoxDecoration(
// borderRadius: BorderRadius.circular(14),
border: Border(
bottom: BorderSide(width: 1, color: Colors.black38),
),
),
buttonPadding: const EdgeInsets.only(bottom: 5, top: 5),
),
)
],
),
)
);
}
}
I've tried to populate the dropdown menu button with the data from the SQLite database.
Then on the onTap Function I wanted to navigate to the selected category.
When I tap on the category it does not navigate.
I have saved each category with an id in the database which is used the identify the selected item.
Here is the code:
'''
class _HomeState extends State<Home> {
TodoService _todoService;
var _selectedValue;
var _categories = List<DropdownMenuItem>();
List<Todo>_todoList=List<Todo>();
#override
initState(){
super.initState();
_loadCategories();
}
_loadCategories() async {
var _categoryService = CategoryService();
var categories = await _categoryService.readCategory();
categories.forEach((category) {
setState(() {
_categories.add(DropdownMenuItem(
child: Text(category['name']),
value: category['name'],
onTap: ()=>Navigator.of(context).push(MaterialPageRoute(builder:(context)=>TodosByCategory(category: category['name'],))),
));
});
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
key: _globalKey,
appBar: AppBar(
actions: <Widget>[
DropdownButtonHideUnderline(
child: DropdownButton(
value: _selectedValue,
items: _categories,
dropdownColor: Colors.blue,
style: TextStyle(color: Colors.white,fontSize: 16.0),
iconDisabledColor: Colors.white,
iconEnabledColor: Colors.white,
onChanged: (value) {
setState(() {
_selectedValue = value;
});
},
),
),
'''
Here is the todosByCategory():
'''
class _TodosByCategoryState extends State<TodosByCategory> {
List<Todo>_todoList=List<Todo>();
TodoService _todoService=TodoService();
#override
initState(){
super.initState();
getTodosByCategories();
}
getTodosByCategories()async{
var todos=await _todoService.readTodoByCategory(this.widget.category);
todos.forEach((todo){
setState(() {
var model= Todo();
model.title=todo['title'];
model.dueDate=todo['dueDate'];
_todoList.add(model);
});
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Todos By Category'),
),
body: Column(
children: <Widget>[
Expanded(
child: ListView.builder(
itemCount: _todoList.length,
itemBuilder: (context, index){
return Padding(
padding: EdgeInsets.only(top:8.0, left: 8.0, right: 8.0),
child: Card(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(0),
),
elevation: 8.0,
child: ListTile(
title: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Text(_todoList[index].title)
],
),
subtitle: Text(_todoList[index].dueDate),
// trailing: Text(_todoList[index].dueDate),
),
),
);
},),
)
],
),
);
}
}
'''
Please help me out.
Instead of writing the navigation code inside onTap of DropdownMenuItem, you can write it inside onChanged of DropdownButton where you are also getting the category name string as the value. It should work then.
I want to display 2 radio buttons horizontally on the subtitle of the ListTile.
I can see only one Radio Button.
List<QuestionsOptions> optionsList = [
QuestionsOptions(
index: 1,
name: "Yes",
),
QuestionsOptions(
index: 0,
name: "No",
),
];
subtitle:Column(
children: [
new Row(
children: <Widget>[
Expanded(
child: Container(
child: Column(
children:
optionsList.map((data) => RadioListTile(
title: Text("${data.name}"),
groupValue: 0,
value: data.index,
onChanged: (val) {
//_handleWeightChange(data.name,data.index);
},
)).toList(),
),
)),
],
),
]),
How to display both the radio buttons in horizontal in the subtitle of the Listtile
You can set a Row of RadioListTile in the subTitle of the ListTile. You can then use HashMap to track the selected RadioListTile on the List items. The map can be updated when the radio button is clicked. Or if you're using a List for your ListTiles, you can just add the Answer enum on the Object.
subtitle: Padding(
padding: const EdgeInsets.only(top: 8.0),
child: Row(
children: [
Expanded(
child: RadioListTile<Answer>(
title: const Text('Yes'),
value: Answer.yes,
groupValue: answerVal[index],
onChanged: (Answer? value) {
setState(() {
// Update map value on tap
answerVal[index] = value;
});
},
),
),
Expanded(
child: RadioListTile<Answer>(
title: const Text('No'),
value: Answer.no,
groupValue: answerVal[index],
onChanged: (Answer? value) {
setState(() {
answerVal[index] = value;
});
},
),
),
],
),
),
Here's the complete sample
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
#override
State<MyHomePage> createState() => _MyHomePageState();
}
enum Answer { yes, no }
class _MyHomePageState extends State<MyHomePage> {
Map<int, Answer?> answerVal = {};
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: ListView.builder(
itemCount: 10,
itemBuilder: (BuildContext context, int index) {
return ListTile(
contentPadding: const EdgeInsets.all(16.0),
title: const Text('Question?'),
subtitle: Padding(
padding: const EdgeInsets.only(top: 8.0),
child: Row(
children: [
Expanded(
child: RadioListTile<Answer>(
title: const Text('Yes'),
value: Answer.yes,
groupValue: answerVal[index],
onChanged: (Answer? value) {
setState(() {
answerVal[index] = value;
});
},
),
),
Expanded(
child: RadioListTile<Answer>(
title: const Text('No'),
value: Answer.no,
groupValue: answerVal[index],
onChanged: (Answer? value) {
setState(() {
answerVal[index] = value;
});
},
),
),
],
),
),
);
},
),
);
}
}