Flutter dropdown with getX not working properly on some devices - flutter

I'm using getx for state management,here's the video of the problem
https://drive.google.com/file/d/1tm2M46pkXVnGuf4vyh9rNs2HY2TdtBD8/view?usp=sharing
here is my code
class ActivitiesController extends GetxController {
late List<String> statusList = ["All", "Approved", "Unapproved"];
var selectedStatus = "Approved".obs;
}
#override
ActivitiesController get controller => Get.put(ActivitiesController());
in view:
const RequiredText(text: "Status"),
const SizedBox(height: Constants.defaultPadding / 2),
Obx(
() => GlobalDropDownContainer(
hintText: "All",
items: controller.statusList.toList(),
onChange: (value) {
controller.selectedStatus(value);
},
selectedValue: controller.selectedStatus.value,
)),
Here is the "GlobalDropDownContainer" code
import 'package:flutter/material.dart';
import 'package:get/get.dart';
class GlobalDropDownContainer extends StatelessWidget {
final String hintText;
final List<String> items;
final double? width;
final Color? isNotValid;
final Function(String?) onChange;
final String? selectedValue;
const GlobalDropDownContainer({
Key? key,
required this.hintText,
required this.items,
this.width,
this.isNotValid,
required this.onChange,
this.selectedValue,
}) : super(key: key);
#override
Widget build(BuildContext context) {
Size size = Get.size;
return Container(
width: width != null ? (size.width * width!) : Get.width,
padding: const EdgeInsets.symmetric(horizontal: 10),
decoration: BoxDecoration(
border: Border.all(
width: 1,
color: isNotValid ??
Theme.of(context).colorScheme.onSurface.withOpacity(0.5),
),
color:Theme.of(context).colorScheme.background,
borderRadius: BorderRadius.circular(5)),
child: DropdownButton<String>(
dropdownColor: Theme.of(context).colorScheme.background,
value: selectedValue != null && selectedValue!.isNotEmpty
? selectedValue
: null,
isExpanded: true,
underline: const SizedBox(),
hint: Text(
hintText,
style: const TextStyle(
color: Color(0xFF666666),
),
),
style: TextStyle(
color: Theme.of(context).colorScheme.onBackground,
),
items: items.map((String value) {
return DropdownMenuItem<String>(
value: value != null && value.isNotEmpty ? value : null,
child: Text(
value,
style: TextStyle(
color: Theme.of(context).colorScheme.onBackground),
),
);
}).toList(),
onChanged: onChange,
));
}
}
I searched for a day and i didn't find anything, i tried debug the code but it gives no warning or error. Can Anyone help me?

Try this one
Make an instance of a controller, which i presumed u have done already.
final controller = Get.put(yourgetxcontrollername());
The Widget code
Obx(
() => DropdownButton<String>(
isExpanded: true,
value: controller.selectedStatus.value,
icon: const Icon(Icons.arrow_drop_down),
iconSize: 24,
elevation: 16,
style: const TextStyle(
color: Colors.blue,
fontSize: 14,
),
onChanged: (value) {
controller.selectedStatus(
value,
);
},
items: controller.statusList.map<DropdownMenuItem<String>>(
(String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(
value,
style: const TextStyle(
color: Colors.black,
),
),
);
},
).toList(),
),
)

Related

I cant seem to update or edit my Todo app

My customer app (whereby you add customer details to a list) successfully adds and deletes data to the data base and sets the state properly. But I cannot get my edit/update function to work.
When I click my edit icon, it successfully shows the details of the existing data, but when I change the detail (text) it does not update to set the state with the new data.
The functions insertTodo, deleteTodo and updateTodo are the problem:
Library
import 'package:sqflite/sqflite.dart';
import 'package:path/path.dart';
class Todo {
int? id;
final String title;
final String name;
final String phone;
final String fax;
final String email;
final String street;
final String city;
final String town;
final String code;
bool isExpanded;
Todo({
this.id,
required this.title,
required this.name,
required this.phone,
required this.fax,
required this.email,
required this.street,
required this.city,
required this.town,
required this.code,
this.isExpanded = false,
});
Map<String, dynamic> toMap() {
return {
'id': id,
'title': title,
'name': name,
'phone': phone,
'fax': fax,
'email': email,
'street': street,
'city': city,
'town': town,
'code': code,
'isExpanded': isExpanded ? 1 : 0,
};
}
#override
String toString() {
return 'Todo(id : $id, title : $title, name : $name, phone : $phone, fax: $fax, email:
$email, street: $street, city: $city, town: $town, code: $code, isExpanded :
$isExpanded,)';
}
}
class DatabaseConnect {
Database? _database;
Future<Database> get database async {
final dbpath = await getDatabasesPath();
const dbname = 'todo.db';
final path = join(dbpath, dbname);
_database = await openDatabase(path, version: 1, onCreate: _createDB);
return _database!;
}
Future<void> _createDB(Database db, int version) async {
await db.execute('''
CREATE TABLE todo(
id INTEGER PRIMARY KEY AUTOINCREMENT,
title TEXT,
name TEXT,
phone TEXT,
fax TEXT,
email TEXT,
street TEXT,
city TEXT,
town TEXT,
code TEXT,
isExpanded INTEGER
)
''');
}
Future<void> insertTodo(Todo todo) async {
final db = await database;
await db.insert(
'todo',
todo.toMap(),
conflictAlgorithm: ConflictAlgorithm.replace,
);
}
Future<void> deleteTodo(Todo todo) async {
final db = await database;
await db.delete(
'todo',
where: 'id == ?',
whereArgs: [todo.id],
);
}
Future<void> updateTodo(Todo todo) async {
final db = await database;
await db.update(
'todo',
todo.toMap(),
where: 'id = ?',
whereArgs: [todo.id],
);
}
Future<List<Todo>> getTodo() async {
final db = await database;
List<Map<String, dynamic>> items = await db.query(
'todo',
orderBy: 'title ASC',
);
return List.generate(
items.length,
(i) => Todo(
id: items[i]['id'],
title: items[i]['title'],
name: items[i]['name'],
phone: items[i]['phone'],
fax: items[i]['fax'],
email: items[i]['email'],
street: items[i]['street'],
city: items[i]['city'],
town: items[i]['town'],
code: items[i]['code'],
isExpanded: items[i]['isExpanded'] == 1 ? true : false,
),
);
}
Future<List<Todo>> searchContacts(String keyword) async {
final db = await database;
List<Map<String, dynamic>> items =
await db.query('todo', where: 'title LIKE ?', whereArgs: ['$keyword%']);
return List.generate(
items.length,
(i) => Todo(
id: items[i]['id'],
title: items[i]['title'],
name: items[i]['name'],
phone: items[i]['phone'],
fax: items[i]['fax'],
email: items[i]['email'],
street: items[i]['street'],
city: items[i]['city'],
town: items[i]['town'],
code: items[i]['code'],
isExpanded: items[i]['isExpanded'] == 1 ? true : false,
),
);
}
}
Following is my Customer page, where the functions addItem, deleteItem, updateItem are called that link to the database and also the page where I setState (the mainframe the app goes back to after any action is taken). It portrays the list of data after a new customer detail gets added, existing detail deleted and new info updated:
Customer
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import '../customerlist.dart';
import '../library.dart';
import '../user_input.dart';
class Customer extends StatefulWidget {
const Customer({Key? key}) : super(key: key);
#override
_CustomerState createState() => _CustomerState();
}
class _CustomerState extends State<Customer> {
var db = DatabaseConnect();
void addItem(Todo todo) async {
await db.insertTodo(todo);
setState(() {});
}
void deleteItem(Todo todo) async {
await db.deleteTodo(todo);
setState(() {});
}
void updateItem(Todo todo) async {
await db.updateTodo(todo);
setState(() {});
}
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
appBar: AppBar(
centerTitle: false,
title: const Padding(
padding: EdgeInsets.all(50.0),
child: Text(
'My Clients',
style: TextStyle(
fontSize: 24,
fontWeight: FontWeight.w600,
color: Colors.black),
),
),
backgroundColor: Colors.white,
elevation: 0,
actions: [
IconButton(
onPressed: () {
Navigator.of(context).pushReplacementNamed('/searchPage');
},
icon: const Icon(
Icons.search,
color: Colors.black,
),
),
]),
body: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Padding(
padding: EdgeInsets.symmetric(horizontal: 16, vertical: 10),
child: Text(
'Company Name',
style: TextStyle(fontSize: 14, fontWeight: FontWeight.w600),
),
),
CustomerList(
// insertFunction: addItem,
tobie: deleteItem,
stella: updateItem,
),
],
),
floatingActionButton: FloatingActionButton(
backgroundColor: Colors.lightBlueAccent,
child: const Icon(Icons.add),
onPressed: () {
showModalBottomSheet(
isScrollControlled: true,
context: context,
builder: (context) => CustomerProfile(insertFunction: addItem),
);
}),
floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat,
);
}
}
The next code is my CustomerList where my ListviewBuilder checks for customer data to display. If there are, passes the detail to my CustomerCard page (and populate into a card/ListTile). Also in my CustomerList it passes my additem through function Stella from Customer, through CustomerList, through CustomerCard, to my EditPage:
CustomerList
import 'package:flutter/material.dart';
import 'library.dart';
import 'customercard.dart';
class CustomerList extends StatelessWidget {
final Function tobie;
final Function stella;
final db = DatabaseConnect();
CustomerList(
{required this.stella,
required this.tobie,
Key? key})
: super(key: key);
#override
Widget build(BuildContext context) {
return Expanded(
child: FutureBuilder(
future: db.getTodo(),
initialData: const [],
builder: (BuildContext context, AsyncSnapshot<List> snapshot) {
var data = snapshot
.data; // this is the data we have to show. (list of todo)
var datalength = data!.length;
return datalength == 0
? const Center(
child: Text('no data found'),
)
: ListView.builder(
itemCount: datalength,
itemBuilder: (context, i) => CustomerCard(
id: data[i].id,
title: data[i].title,
name: data[i].name,
phone: data[i].phone,
fax: data[i].fax,
email: data[i].email,
street: data[i].street,
city: data[i].city,
town: data[i].town,
code: data[i].code,
isExpanded: data[i].isExpanded,
// insertFunction: insertFunction,
janFunction: tobie,
simbaFunction: stella,
),
);
}),
);
}
}
Following is my CustomerCard code, which populates the data into a Card and ListTile. Once again, my updateItem is passed through (simbaFunction:stella):
CustomerCard
import 'package:flutter/material.dart';
import 'library.dart';
import 'package:test_sqlite/edit_input.dart';
class CustomerCard extends StatefulWidget {
final int id;
final String title;
final String name;
final String phone;
final String fax;
final String email;
final String street;
final String city;
final String town;
final String code;
bool isExpanded;
final Function janFunction;
final Function simbaFunction;
CustomerCard(
{required this.id,
required this.title,
required this.name,
required this.phone,
required this.fax,
required this.email,
required this.street,
required this.city,
required this.town,
required this.code,
required this.isExpanded,
required this.janFunction,
required this.simbaFunction,
Key? key})
: super(key: key);
#override
_CustomerCardState createState() => _CustomerCardState();
}
class _CustomerCardState extends State<CustomerCard> {
var db = DatabaseConnect();
#override
Widget build(BuildContext context) {
var anotherTodo = Todo(
id: widget.id,
title: widget.title,
name: widget.name,
phone: widget.phone,
fax: widget.fax,
email: widget.email,
street: widget.street,
city: widget.city,
town: widget.town,
code: widget.code,
isExpanded: widget.isExpanded);
return Card(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Theme(
data: Theme.of(context).copyWith(dividerColor: Colors.transparent),
child: ExpansionTile(
initiallyExpanded: false,
title: Text(
widget.title,
style: const TextStyle(
//fontWeight: FontWeight.bold,
fontSize: 16,
),
),
children: [
ListTile(
leading: const Icon(
Icons.person,
size: 20,
color: Colors.teal,
),
visualDensity: const VisualDensity(vertical: -3),
title: Text(
widget.name,
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.normal,
color: Colors.black,
),
),
),
ListTile(
leading: const Icon(
Icons.phone,
size: 20,
color: Colors.teal,
),
visualDensity: const VisualDensity(vertical: -4),
title: Text(
widget.phone,
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.normal,
color: Colors.black,
),
),
),
ListTile(
leading: const Icon(
Icons.report,
size: 20,
color: Colors.teal,
),
visualDensity: const VisualDensity(vertical: -4),
title: Text(
widget.fax,
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.normal,
color: Colors.black,
),
),
),
ListTile(
leading: const Icon(
Icons.email,
size: 20,
color: Colors.teal,
),
visualDensity: const VisualDensity(vertical: -4),
title: Text(
widget.email,
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.normal,
color: Colors.black,
),
),
),
ListTile(
leading: const Icon(
Icons.place,
size: 20,
color: Colors.teal,
),
visualDensity: const VisualDensity(vertical: -4),
title: Text(
widget.street,
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.normal,
color: Colors.black,
),
),
),
ListTile(
leading: const Icon(
Icons.place,
size: 20,
color: Colors.teal,
),
visualDensity: const VisualDensity(vertical: -4),
title: Text(
widget.city,
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.normal,
color: Colors.black,
),
),
),
ListTile(
leading: const Icon(
Icons.place,
size: 20,
color: Colors.teal,
),
visualDensity: const VisualDensity(vertical: -4),
title: Text(
widget.town,
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.normal,
color: Colors.black,
),
),
),
ListTile(
leading: const Icon(
Icons.code,
size: 20,
color: Colors.teal,
),
visualDensity: const VisualDensity(vertical: -4),
title: Text(
widget.code,
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.normal,
color: Colors.black,
),
),
),
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: ElevatedButton(
onPressed: () {
showModalBottomSheet(
isScrollControlled: true,
context: context,
builder: (context) => EditPage(
nalaFunction: widget.simbaFunction,
variable: anotherTodo,
edit: '',
edit1: '',
edit2: '',
edit3: '',
edit4: '',
edit5: '',
edit6: '',
edit7: '',
edit8: '',
),
//updateFunction,
//),
);
},
style: ElevatedButton.styleFrom(
shape: const StadiumBorder(),
primary: Colors.white,
elevation: 0,
padding: const EdgeInsets.symmetric(
horizontal: 2, vertical: 2),
),
child: const Icon(
Icons.edit,
size: 20,
color: Colors.grey,
),
),
),
],
),
],
leading: const IconButton(
icon: Icon(
Icons.place,
color: Colors.blue,
size: 20,
),
onPressed: null,
alignment: Alignment.center,
),
trailing: IconButton(
onPressed: () {
widget.janFunction(anotherTodo);
},
icon: const Icon(
Icons.delete,
color: Colors.red,
size: 20,
),
),
),
),
],
),
);
}
}
Lastly, my EditPage. When I open it, the existing customer data in the SQLite database automatically displays correctly. Each Textfield has its own onChanged. Once I press the Elevated button, the bottomModalSheet closes properly (pop), but the new data does not actually update. This is the problem.
EditPage
import 'package:flutter/material.dart';
import 'library.dart';
import 'package:flutter/cupertino.dart';
class EditPage extends StatelessWidget {
final Todo variable;
final Function nalaFunction;
String edit;
String edit1;
String edit2;
String edit3;
String edit4;
String edit5;
String edit6;
String edit7;
String edit8;
late final textController = TextEditingController(text: variable.title);
late final nameController = TextEditingController(text: variable.name);
late final phoneController = TextEditingController(text: variable.phone);
late final faxController = TextEditingController(text: variable.fax);
late final emailController = TextEditingController(text: variable.email);
late final streetController = TextEditingController(text: variable.street);
late final cityController = TextEditingController(text: variable.city);
late final townController = TextEditingController(text: variable.town);
late final codeController = TextEditingController(text: variable.code);
EditPage(
{Key? key,
required this.nalaFunction,
required this.variable,
required this.edit,
required this.edit1,
required this.edit2,
required this.edit3,
required this.edit4,
required this.edit5,
required this.edit6,
required this.edit7,
required this.edit8,
}) : super(key: key);
#override
Widget build(BuildContext context) {
return Container(
padding: const EdgeInsets.all(30.0),
decoration: const BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.only(
topLeft: Radius.circular(30.0),
topRight: Radius.circular(30.0),
),
),
child: Scaffold(
appBar: AppBar(
backgroundColor: Colors.white,
elevation: 0,
title: const Padding(
padding: EdgeInsets.all(15.0),
child: Text(
'Client Details',
style: TextStyle(color: Colors.black, fontSize: 24),
),
),
leading: GestureDetector(
onTap: () {
Navigator.of(context).pushReplacementNamed('/homePage');
},
child: const Icon(
Icons.arrow_back,
color: Colors.black,
),
),
),
body: SingleChildScrollView(
child: Container(
color: Colors.white,
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
TextField(
controller: textController,
autofocus: true,
textAlign: TextAlign.left,
decoration: const InputDecoration(
hintText: 'Company Name',
hintStyle: TextStyle(color: Colors.grey),
),
onChanged: (String? value) {
edit = value!;
},
),
TextField(
controller: nameController,
autofocus: true,
textAlign: TextAlign.left,
decoration: const InputDecoration(
hintText: ' Contact Name & Surname',
hintStyle: TextStyle(color: Colors.grey),
),
onChanged: (String? value1) {
edit1 = value1!;
},
),
TextField(
controller: phoneController,
autofocus: true,
textAlign: TextAlign.left,
decoration: const InputDecoration(
hintText: ' Contact Number',
hintStyle: TextStyle(color: Colors.grey),
),
onChanged: (String? value2) {
edit2 = value2!;
},
),
TextField(
controller: faxController,
autofocus: true,
textAlign: TextAlign.left,
decoration: const InputDecoration(
hintText: 'Fax Number',
hintStyle: TextStyle(color: Colors.grey),
),
onChanged: (String? value3) {
edit3 = value3!;
},
),
TextField(
controller: emailController,
autofocus: true,
textAlign: TextAlign.left,
decoration: const InputDecoration(
hintText: 'Email Address',
hintStyle: TextStyle(color: Colors.grey),
),
onChanged: (String? value4) {
edit4 = value4!;
},
),
TextField(
controller: streetController,
autofocus: true,
textAlign: TextAlign.left,
decoration: const InputDecoration(
hintText: 'Street Name',
hintStyle: TextStyle(color: Colors.grey),
),
onChanged: (String? value5) {
edit5 = value5!;
},
),
TextField(
controller: cityController,
autofocus: true,
textAlign: TextAlign.left,
decoration: const InputDecoration(
hintText: ' City',
hintStyle: TextStyle(color: Colors.grey),
),
onChanged: (String? value6) {
edit6 = value6!;
},
),
TextField(
controller: townController,
autofocus: true,
textAlign: TextAlign.left,
decoration: const InputDecoration(
hintText: 'Town',
hintStyle: TextStyle(color: Colors.grey),
),
onChanged: (String? value7) {
edit7 = value7!;
},
),
TextField(
controller: codeController,
autofocus: true,
textAlign: TextAlign.left,
decoration: const InputDecoration(
hintText: ' Code',
hintStyle: TextStyle(color: Colors.grey),
),
onChanged: (String? value8) {
edit8 = value8!;
},
),
ElevatedButton(
onPressed: () {
Navigator.pop(context);
var valueTodo = Todo(
title: edit,
name: edit1,
phone: edit2,
fax: edit3,
email: edit4,
street: edit5,
city: edit6,
town: edit7,
code: edit8,
isExpanded: false);
nalaFunction(valueTodo);
},
child: Padding(
padding: const EdgeInsets.fromLTRB(0, 10, 0, 0),
child: Container(
decoration: BoxDecoration(
color: Theme.of(context).primaryColor,
borderRadius: BorderRadius.circular(15),
),
padding: const EdgeInsets.symmetric(
horizontal: 25, vertical: 10),
child: const Text(
'Update',
textAlign: TextAlign.center,
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
),
),
),
),
),
],
),
),
),
),
);
}
}
I think the problem is either:
My function in my database is incorrect.
Somehow the data (updateItem) in Customer page does not link with updateTodo in the database.
The updateItem data is passed through all the widgets to EditPage incorrectly.
My code in EditPage (ElevatedButton onPressed is wrong).
The argument (edit '', edit1 '' and so on) in CustomerCard page that route to EditPage is wrong.
I have managed to finally resolve the problem. I have replaced my ElevatedButton with a GestureDetector and also removed onChaged from my TextFields, thus also removing all my edit parameters.
My updateFunction and updateItem function was actually working just fine all along. I previously used GestureDetector with onTap and a variable (updateTodo) with my textEditingControllers, but since I could not make this work, I tried ElevateButton with the onChanged, which also did not work. So, moving back to GestureDetector, I realized that I never assigned an 'id' to my variable ' updateTodo'. I only had my TextEditingController. Now, in my Sql Database, the database is updated using 'Where: id', but since I did not assign an 'id' to my text, I effectively returned Null everytime I updated.
Here is the snippet of code for GetureDetector before the fix,
GestureDetector(
onTap: () {
Navigator.pop(context);
var updateTodo = Todo(
title: textController.text,
name: nameController.text,
phone: phoneController.text,
fax: faxController.text,
email: emailController.text,
street: streetController.text,
city: cityController.text,
town: townController.text,
code: codeController.text,
isExpanded: false);
nalaFunction(updateTodo);
},
and here the same code after the fix:
GestureDetector(
onTap: () {
Navigator.pop(context);
var updateTodo = Todo(
id: id,
title: textController.text,
name: nameController.text,
phone: phoneController.text,
fax: faxController.text,
email: emailController.text,
street: streetController.text,
city: cityController.text,
town: townController.text,
code: codeController.text,
isExpanded: false);
nalaFunction(updateTodo);
},
and the code for passing the 'id' in CustomerCard class through to EditPage:
class CustomerCard extends StatefulWidget {
final int id;
final String title;
final String name;
final String phone;
final String fax;
final String email;
final String street;
final String city;
final String town;
final String code;
bool isExpanded;
final Function janFunction;
final Function simbaFunction;
CustomerCard(
{required this.id,
required this.title,
required this.name,
required this.phone,
required this.fax,
required this.email,
required this.street,
required this.city,
required this.town,
required this.code,
required this.isExpanded,
and ..
Row(mainAxisAlignment: MainAxisAlignment.end,
children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: ElevatedButton(
onPressed: () {
showModalBottomSheet(
isScrollControlled: true,
context: context,
builder: (context) => EditPage(
nalaFunction: widget.simbaFunction,
variable: anotherTodo,
id: widget.id,
),
//updateFunction,
//),
);
},
and passing the 'id' through to the EditPage so that it can be reached by the GestureDetector on the same page:
class EditPage extends StatelessWidget {
final Todo variable;
final Function nalaFunction;
final int id;
late final textController = TextEditingController(text: variable.title);
late final nameController = TextEditingController(text: variable.name);
late final phoneController = TextEditingController(text: variable.phone);
late final faxController = TextEditingController(text: variable.fax);
late final emailController = TextEditingController(text: variable.email);
late final streetController = TextEditingController(text: variable.street);
late final cityController = TextEditingController(text: variable.city);
late final townController = TextEditingController(text: variable.town);
late final codeController = TextEditingController(text: variable.code);
EditPage({Key? key,
required this.nalaFunction,
required this.variable,
required this.id,
}) : super(key: key);
This resolved my problem and my app now update new customer data perfectly.

how to make switches individually marked? Flutter

I'm making a list of notifications using switches (there will be fifteen in total), but the way I did they turn them all on and off together, how do I turn them on and off individually? And do they accept refactoring to make the code cleaner?
I'm using SwitchListTile.
class CardButton extends StatefulWidget {
const CardButton({Key? key}) : super(key: key);
#override
State<CardButton> createState() => _CardButtonState();
}
class _CardButtonState extends State<CardButton> {
bool _toggled = false;
#override
Widget build(BuildContext context) {
return Column(
children: [
Card(
child: SwitchListTile(
contentPadding: EdgeInsets.only(left: 16.0),
title: Text(
'botton',
style: TextStyle(
color: Colors.black,
),
),
value: _toggled,
onChanged: (bool value) {
setState(() => _toggled = value);
},
),
),
Card(
child: SwitchListTile(
contentPadding: EdgeInsets.only(left: 16.0),
title: Text(
'botton',
style: TextStyle(
color: Colors.black,
),
),
value: _toggled,
onChanged: (bool value) {
setState(() => _toggled = value);
},
),
),
Card(
child: SwitchListTile(
contentPadding: EdgeInsets.only(left: 16.0),
title: Text(
'botton',
style: TextStyle(
color: Colors.black,
),
),
value: _toggled,
onChanged: (bool value) {
setState(() => _toggled = value);
},
),
),
],
);
}
}
You need to create variables to hold the switch state for each switch (toggle) - in your case 15 in total.
From your sample code with individual values for each switch:
class CardButton extends StatefulWidget {
const CardButton({Key? key}) : super(key: key);
#override
State<CardButton> createState() => _CardButtonState();
}
class _CardButtonState extends State<CardButton> {
bool _switch1Toggled = false;
bool _switch2Toggled = false;
bool _switch3Toggled = false;
#override
Widget build(BuildContext context) {
return Column(
children: [
Card(
child: SwitchListTile(
contentPadding: EdgeInsets.only(left: 16.0),
title: Text(
'switch 1',
style: TextStyle(
color: Colors.black,
),
),
value: _switch1Toggled,
onChanged: (bool value) {
setState(() => _switch1Toggled = value);
},
),
),
Card(
child: SwitchListTile(
contentPadding: EdgeInsets.only(left: 16.0),
title: Text(
'switch 2',
style: TextStyle(
color: Colors.black,
),
),
value: _switch2Toggled,
onChanged: (bool value) {
setState(() => _switch2Toggled = value);
},
),
),
Card(
child: SwitchListTile(
contentPadding: EdgeInsets.only(left: 16.0),
title: Text(
'switch 3',
style: TextStyle(
color: Colors.black,
),
),
value: _switch3Toggled,
onChanged: (bool value) {
setState(() => _switch3Toggled = value);
},
),
),
],
);
}
}
Thank you very much for your tip Ranvir Mohanlal. I created this template based on your information. I think it worked better.
class MultiSwitch extends StatefulWidget {
const MultiSwitch({Key? key}) : super(key: key);
#override
State<MultiSwitch> createState() => _MultiSwitchState();
}
class _MultiSwitchState extends State<MultiSwitch> {
bool val1 = true;
bool val2 = false;
bool val3 = false;
onChangeFunction1(bool newValue1) {
setState(() {
val1 = newValue1;
});
}
onChangeFunction2(bool newValue2) {
setState(() {
val2 = newValue2;
});
}
onChangeFunction3(bool newValue3) {
setState(() {
val3 = newValue3;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
customSwitch('button', val1, onChangeFunction1),
customSwitch('button', val2, onChangeFunction2),
customSwitch('button', val3, onChangeFunction3),
],
),
);
}
}
Widget customSwitch(String text, bool val, Function onChangeMethod) {
return Card(
child: SwitchListTile(
title: Text(
text,
style: const TextStyle(
color: Colors.black,
fontSize: 18,
),
),
value: val,
onChanged: (newValue) {
onChangeMethod(newValue);
}
),
);
}

How to implement dropdownbutton into a function in flutter

Im working on a company project. But I can't simply get the idea of how to implement a basic dropdown button into a function but I can't seem to make the values change in the dropdown function what do you think im doing wrong here's my code:
Widget buildDropdownField({
required String dropdownHeader,
required String dropdownValue,
}) {
return Column(
children: <Widget>[
Text(dropdownHeader),
const SizedBox(
height: 10,
),
//dropdownField
DropdownButton<String>(
value: dropdownValue,
icon: const Icon(Icons.arrow_downward),
elevation: 16,
style: const TextStyle(color: Colors.deepPurple),
underline: Container(
height: 2,
color: Colors.deepPurpleAccent,
),
onChanged: (String? newValue) {
setState(() {
dropdownValue = newValue!;
});
},
items: <String>['-', 'Geçti', 'Kaldı', 'Belirsiz']
.map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
}).toList(),
)
],
);
}
Wrap with StatefulBuilder it will work.
Widget buildDropdownField(
{required String dropdownHeader, required String dropdownValue}) {
return Column(
children: <Widget>[
Text(dropdownHeader),
const SizedBox(
height: 10,
),
StatefulBuilder(
builder: (_, setDropState) {
return DropdownButton<String>(
value: dropdownValue,
icon: const Icon(Icons.arrow_downward),
elevation: 16,
style: const TextStyle(color: Colors.deepPurple),
underline: Container(
height: 2,
color: Colors.deepPurpleAccent,
),
onChanged: (String? newValue) {
setDropState(() {
dropdownValue = newValue!;
});
},
items: <String>['-', 'Geçti', 'Kaldı', 'Belirsiz']
.map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
}).toList(),
);
},
)
],
);
}
Try below code hope its help to you. use StatefulBuilder Refer here
Your dropdown function:
buildDropdownField({
required String dropdownHeader,
required String dropdownValue,
}) {
return Column(
children: <Widget>[
Text(dropdownHeader),
const SizedBox(
height: 10,
),
//dropdownField
StatefulBuilder(builder: (context, StateSetter setState) {
return DropdownButton<String>(
value: dropdownValue,
icon: const Icon(Icons.arrow_downward),
elevation: 16,
style: const TextStyle(color: Colors.deepPurple),
underline: Container(
height: 2,
color: Colors.deepPurpleAccent,
),
onChanged: (String? newValue) {
setState(() {
dropdownValue = newValue!;
});
},
items: <String>['-', 'Geçti', 'Kaldı', 'Belirsiz']
.map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
}).toList(),
);
})
],
);
}
Your Widget:
buildDropdownField(
dropdownHeader: 'dropdownHeader',
dropdownValue: '-',
),
Result->
Result after selection->
First of all, you shouldn't update the parameter with the new value. It did update the parameter, but the function will still getting the value from it's calling.
I did not know the buildDropdownField function is inside a class or not, but it's okay and I will provide the solutions for both scenarios.
Within the Class
You need to create a variable within a class outside the functions.
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
String _dropDownValue = '-';
Widget buildDropdownField({required String dropdownHeader}) {
return Column(
children: <Widget>[
Text(dropdownHeader),
const SizedBox(
height: 10,
),
//dropdownField
DropdownButton<String>(
value: _dropDownValue,
icon: const Icon(Icons.arrow_downward),
elevation: 16,
style: const TextStyle(color: Colors.deepPurple),
underline: Container(
height: 2,
color: Colors.deepPurpleAccent,
),
onChanged: (String? newValue) {
setState(() {
_dropDownValue = newValue!;
});
},
items: <String>['-', 'Geçti', 'Kaldı',
'Belirsiz'].map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
}).toList(),
)
],
);
}
}
Outside the Class
You need to turn it into Stateful Widget in order for the drop down text to change. Once the dropdown is a stateful widget, you can use the solution above or a callback to make the changes on parent class.
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
String _dropDownValue = '-';
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: DropDownWidget(
dropdownHeader: 'Name',
dropdownValue: _dropDownValue,
onChanged: (String? newValue) {
setState(() {
_dropDownValue = newValue!;
});
},
),
),
);
}
}
class DropDownWidget extends StatefulWidget {
final String dropdownHeader;
final String dropdownValue;
final Function(String?)? onChanged;
DropDownWidget({required this.dropdownHeader, required this.dropdownValue, this.onChanged, Key? key}) : super(key: key);
#override
_DropDownWidgetState createState() => _DropDownWidgetState();
}
class _DropDownWidgetState extends State<DropDownWidget> {
#override
Widget build(BuildContext context) {
return Column(
children: <Widget>[
Text(widget.dropdownHeader),
const SizedBox(
height: 10,
),
//dropdownField
DropdownButton<String>(
value: widget.dropdownValue,
icon: const Icon(Icons.arrow_downward),
elevation: 16,
style: const TextStyle(color: Colors.deepPurple),
underline: Container(
height: 2,
color: Colors.deepPurpleAccent,
),
onChanged: widget.onChanged,
items: <String>['-', 'Geçti', 'Kaldı', 'Belirsiz'].map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
}).toList(),
)
],
);
}
}

Updating parent form from child using Radio

I am developing a form with 11 multiple choice questions.
Ive created a statefull widget which takes the question and displays this along with 3 radio buttons as below.
Each question needs to update different property in a model defined within the parent widget.
for example:
RadioQuestionWidget("What colour is the sky?", model.ColourOfSky),
RadioQuestionWidget("What colour is the grass?", model.ColourOfGrass)
Below is my RadioQuestionWidget
import 'package:flutter/material.dart';
class RadioQuestionWidget extends StatefulWidget {
RadioQuestionWidget({Key key, this.question}) : super(key: key);
final String question;
#override
_RadioQuestionWidgetState createState() => _RadioQuestionWidgetState();
}
class _RadioQuestionWidgetState extends State<RadioQuestionWidget> {
String question;
var _radioValue;
#override
void initState() {
super.initState();
question = widget.question;
}
#override
Widget build(BuildContext context) {
return Container(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
new Text(
question,
style: new TextStyle(
fontSize: 16.0,
color: Colors.black,
fontWeight: FontWeight.bold),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: Row(
children: <Widget>[
Radio(
value: "Yes",
groupValue: _radioValue,
onChanged: (val) {
setState(() {
_radioValue = val;
});
},
activeColor: Colors.green,
focusColor: Colors.black,
),
new Text(
'Yes',
style: new TextStyle(fontSize: 16.0, color: Colors.black),
),
Radio(
value: "No",
groupValue: _radioValue,
onChanged: (val) {
setState(() {
_radioValue = val;
});
},
activeColor: Colors.green,
focusColor: Colors.black,
),
new Text(
'No',
style: new TextStyle(fontSize: 16.0, color: Colors.black),
),
Radio(
value: "Three",
groupValue: _radioValue,
onChanged: (val) {
setState(() {
_radioValue = val;
});
},
activeColor: Colors.red,
focusColor: Colors.black,
),
new Text(
'Not applicable',
style: new TextStyle(fontSize: 16.0, color: Colors.black),
),
],
),
),
],
),
);
}
}
First of all, define one function in your parent widget with required arguments i.e your question number and answer.
void _updateProperty(int que_num, String ans) {
//update property according to your question number and ans
}
Now pass your function to child widget as the Constructor argument.
RadioQuestionWidget(question : "What colour is the sky?", updatePropertyHandler : _updateProperty)
Receive your function in child widget like below.
class RadioQuestionWidget extends StatefulWidget {
RadioQuestionWidget({Key key, this.question, this.updatePropertyHandler}) : super(key: key);
final String question;
final Function updatePropertyHandler;
#override
_RadioQuestionWidgetState createState() => _RadioQuestionWidgetState();
}
Now in your child widget while you answering the question, call _updateUi function as per your need.
Radio(
value: "Yes",
groupValue: _radioValue,
onChanged: (val) {
setState(() {
_radioValue = val;
//here questionNum is int value you need to handle question no
widget.updatePropertyHandler(questionNum, _radioValue);
});
},
activeColor: Colors.green,
focusColor: Colors.black,
)
Firstly please mark the above answer as the correct one as i couldn't have got it working without the help of #Alpesh.
I had to slightly amend the answer in order to update the correct property of the model.
This in my parent widget:
RadioQuestionWidget(
question: 'Question 1',
updatePropertyHandler: (String ans) => {
setState(() {
_qc.speedForSpeedChaeckCompleted = ans;
})
},
),
and this is my RadioQuestionWidget:
import 'package:flutter/material.dart';
class RadioQuestionWidget extends StatefulWidget {
RadioQuestionWidget({Key key, this.question, this.updatePropertyHandler})
: super(key: key);
final String question;
final Function updatePropertyHandler;
#override
_RadioQuestionWidgetState createState() => _RadioQuestionWidgetState();
}
class _RadioQuestionWidgetState extends State<RadioQuestionWidget> {
String question;
var _groupValue;
Function(String) onCountChange;
#override
void initState() {
super.initState();
question = widget.question;
_groupValue = 'Not Applicable';
}
#override
Widget build(BuildContext context) {
return Container(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
new Text(
question,
style: new TextStyle(
fontSize: 16.0,
color: Colors.black,
fontWeight: FontWeight.bold),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: Row(
children: <Widget>[
Radio(
value: "Yes",
groupValue: _groupValue,
onChanged: (val) {
setState(() {
_groupValue = val;
//here questionNum is int value you need to handle question no
widget.updatePropertyHandler(val);
});
},
activeColor: Colors.green,
focusColor: Colors.black,
),
new Text(
'Yes',
style: new TextStyle(fontSize: 16.0, color: Colors.black),
),
Radio(
value: "No",
groupValue: _groupValue,
onChanged: (val) {
setState(() {
_groupValue = val;
//here questionNum is int value you need to handle question no
widget.updatePropertyHandler(val);
});
},
activeColor: Colors.green,
focusColor: Colors.black,
),
new Text(
'No',
style: new TextStyle(fontSize: 16.0, color: Colors.black),
),
Radio(
value: "Not Applicable",
groupValue: _groupValue,
onChanged: (val) {
setState(() {
_groupValue = val;
//here questionNum is int value you need to handle question no
widget.updatePropertyHandler(val);
});
},
activeColor: Colors.red,
focusColor: Colors.black,
),
new Text(
'Not applicable',
style: new TextStyle(fontSize: 16.0, color: Colors.black),
),
],
),
),
],
),
);
}
}

Set default value for dropdown button in flutter

I have a dropdown button which works fine, but when I try to set a default value it will fail with the following error:
'package:flutter/src/material/dropdown.dart': Failed assertion: line 620 pos 15: 'items == null || items.isEmpty || value == null || items.where((DropdownMenuItem item) => item.value == value).length == 1': is not true.
This is my dropdown button:
Widget changeWorkspace() {
return StatefulBuilder(
builder: (BuildContext context, StateSetter setState) {
return Column(mainAxisSize: MainAxisSize.min, children: <Widget>[
Padding(
padding: EdgeInsets.all(8.0),
child: DropdownButton<AssignedWorkspace>(
isExpanded: true,
hint: Text("SELECT WORKSPACE"),
value: selectedWorkspace,
onChanged: (dropdownValueSelected) {
setState(() {
selectedWorkspace = dropdownValueSelected;
});
},
items: workspaces != null && workspaces.length > 0
? workspaces.map((AssignedWorkspace workspace) {
return new DropdownMenuItem<AssignedWorkspace>(
value: workspace,
child: new Text(workspace.name,
style: new TextStyle(color: Colors.black)),
);
}).toList()
: null),
),
]);
});
}
I've tried to set the value of selectedWorkspace onInit as follows but it fails.
selectedWorkspace = new AssignedWorkspace(
id: userSettings.currentWorkspaceId,
name: userSettings.currentWorkspaceName);
Is there a way of setting a default value in a dropdown button?
import 'package:flutter/material.dart';
import '../config/app_theme.dart';
class DropdownWidget extends StatefulWidget {
final String title;
final List<String> items;
final ValueChanged<String> itemCallBack;
final String currentItem;
final String hintText;
DropdownWidget({
this.title,
this.items,
this.itemCallBack,
this.currentItem,
this.hintText,
});
#override
State<StatefulWidget> createState() => _DropdownState(currentItem);
}
class _DropdownState extends State<DropdownWidget> {
List<DropdownMenuItem<String>> dropDownItems = [];
String currentItem;
AppTheme appTheme;
_DropdownState(this.currentItem);
#override
void initState() {
super.initState();
for (String item in widget.items) {
dropDownItems.add(DropdownMenuItem(
value: item,
child: Text(
item,
style: TextStyle(
fontSize: 16,
),
),
));
}
}
#override
void didUpdateWidget(DropdownWidget oldWidget) {
if (this.currentItem != widget.currentItem) {
setState(() {
this.currentItem = widget.currentItem;
});
}
super.didUpdateWidget(oldWidget);
}
#override
Widget build(BuildContext context) {
appTheme = AppTheme(Theme.of(context).brightness);
return Container(
margin: EdgeInsets.symmetric(vertical: 10),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Container(
margin: EdgeInsets.only(left: 6),
child: Text(
widget.title,
style: appTheme.activityAddPageTextStyle,
),
),
Container(
padding: EdgeInsets.symmetric(vertical: 3, horizontal: 15),
margin: EdgeInsets.only(top: 10),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(6),
color: Colors.white,
boxShadow: [
BoxShadow(
offset: Offset(0, 2),
blurRadius: 10,
color: Color(0x19000000),
),
],
),
child: DropdownButtonHideUnderline(
child: DropdownButton(
icon: appTheme.activityAddPageDownArrowSVG,
value: currentItem,
isExpanded: true,
items: dropDownItems,
onChanged: (selectedItem) => setState(() {
currentItem = selectedItem;
widget.itemCallBack(currentItem);
}),
hint: Container(
child: Text(widget.hintText, style: appTheme.hintStyle),
),
),
),
),
],
),
);
}
}
This is my dropDownWidget without optimization. It has currentItem. You could use it like:
DropdownWidget(
title: kStatus,
items: state.customerStepInfo.statusList,
currentItem: status,
hintText: kCommonPick,
itemCallBack: (String status) {
this.status = status;
},
)
You need implement "equals" in class AssignedWorkspace. I used equatable package.
Example class AssignedWorkspace
class AssignedWorkspace extends Equatable {
final String id;
final String name;
AssignedWorkspace(this.id, this.name);
#override
List<Object> get props => [id];
}
For me id of one of the element is null, once added id is made non-null issue got fixed.
I changed the value of the dropdown var to 1 initially
var _value = '1';
So when the dropdown button has to display its value it displays the one whose value I have set 1 as in the items list in DropDownButton
DropdownButton(
underline: Container(),
onChanged: (value) {
setState(() {
_value = value;
});
},
value: _value,
items: [
DropdownMenuItem(
value: "1",
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
Icon(MaterialCommunityIcons.devices),
SizedBox(width: 10),
Text(
"Consumption",
style: TextStyle(
fontSize: 18.0, fontWeight: FontWeight.w600),
),
],
),
),
DropdownMenuItem(
value: "2",
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
Icon(MaterialCommunityIcons.solar_panel),
SizedBox(width: 10),
Text(
"Generation",
style: TextStyle(
fontSize: 18.0, fontWeight: FontWeight.w600),
),
],
),
),
],
),
if you want to see only an initial value you can use hint text named parameter of drop down button and set a text widget. i dont know whether it is a good practice or not.