I am trying to create a setting page. In it, I would like to have several switches. The user will use them to manage the Permissions, for Camera, Micro and more.
Here is the code I have written, but clearly something is wrong as it is not working properly.
When I tap on the switch, it does not change from On to Off. Also, it does not change the permissions.
I am using:
import 'package:settings_ui/settings_ui.dart';
import 'package:permission_handler/permission_handler.dart';
SizedBox(
height: height,
child: SettingsList(
sections: [
SettingsSection(
title: Text(tPermissions,style: TextStyle(color: Theme.of(context).primaryColor,fontWeight: FontWeight.bold, fontSize: 13.0),
),
tiles: <SettingsTile>[
SettingsTile.switchTile(
leading: const Icon(Icons.camera),
onToggle: (value) {
setState(() {
value = !value;
if (value = false) {
Permission.camera.isDenied;
print (Permission.camera.isDenied);
} else
{
print (Permission.camera.isGranted);
Permission.camera.isGranted;
}
});
},
initialValue: false,
trailing: ChangeCameraWidget(),
title: const Text(tCamera),
),
class ChangeCameraWidget extends StatefulWidget {
const ChangeCameraWidget ({Key? key}) : super(key: key);
#override
State<ChangeCameraWidget> createState() => _ChangeCameraWidgetState();
}
class _ChangeCameraWidgetState extends State<ChangeCameraWidget> {
#override
Widget build(BuildContext context) {
return Switch.adaptive(
value: false,
onChanged: (value) {
if (value = false) {value = true;} else {value = false;}
});
}
}
Related
I'm new to Flutter and I'm working on a localization feature using the easy_localizations package. I created a dropdown that shows languages you can switch to and it works perfectly fine when the dropdown is directly on the page. But since it will be on every page, I want to have the dropdown as a custom widget instead, and then just import it on the pages, like this:
const Padding(
padding: xPadding25,
child: DropDown(),
),
However, that does not work. I'm still able to click on the dropdown and choose a language, but it doesn't translate the pages anymore. I think it has to do something with the context it's translating, but I don't know how to make it so that it will take context of the pages and not its own if it makes sense.
Here's the code for the DropDown widget:
class DropDown extends StatefulWidget {
const DropDown({Key? key}) : super(key: key);
#override
State<DropDown> createState() => _DropDownState();
}
String dropdownValue = 'English';
class _DropDownState extends State<DropDown> {
#override
Widget build(BuildContext context) {
return (DropdownButton<String>(
icon: const Icon(
Icons.language,
color: scTealColor,
),
value: dropdownValue,
elevation: 16,
style: smBodyStyle,
underline: Container(
height: 2,
color: scTealColor,
),
onChanged: (String? newValue) {
setState(() {
dropdownValue = newValue ?? "";
if (dropdownValue == 'French') {
context.setLocale(const Locale('fr'));
} else {
context.setLocale(const Locale('en'));
}
});
},
items: <String>['English', 'French']
.map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
}).toList(),
));
}
}
Any ideas on how to implement it so that the translation works when imported to pages as a custom widget? Thanks!
Have you tried to pass context into your dropdown class ? for i.e.,
const Padding(
padding: xPadding25,
child: DropDown(context),
),
I am getting this error in the console when I am trying to use flutter DropdownButton Widget.
package:flutter/src/material/dropdown.dart': Failed assertion: line 1252 pos 12: 'widget.items!.where((DropdownMenuItem item) => item.value == widget.value).length == 1': is not true.
There is a long traceback...
Here I am adding small code sample that will reproduce this error... Anyone can simply copy paste in main.dart file
// flutter import
import 'package:flutter/material.dart';
void main() {
runApp(const BugReportApp());
}
class BugReportApp extends StatefulWidget {
const BugReportApp({Key? key}) : super(key: key);
#override
State<BugReportApp> createState() => _BugReportAppState();
}
class _BugReportAppState extends State<BugReportApp> {
final TextEditingController _dropdownController = TextEditingController();
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Bug Report',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: Flex(direction: Axis.vertical, children:[
DropdownButton<String>(
value: _dropdownController.text == ""
? null
: _dropdownController.text,
items: ["hello, world", "how are you", "goodbye"]
.map((_value) => DropdownMenuItem<String>(
child: Text(
_value,
)))
.toList(),
onChanged: (_value) {
setState(() {
_dropdownController.text = _value ?? _dropdownController.text;
});
},
),
],),
);
}
}
I was expecting dropown to work normally but, I don't know why it didn't.
You are missing value on DropdownMenuItem.
.map((_value) => DropdownMenuItem<String>(
value: _value, // this
child: Text(
_value,
)))
Also make sure to use Scaffold on home.
Try this code, also added some explanation in the code:
class _MyHomePageState extends State<MyHomePage> {
final TextEditingController _dropdownController = TextEditingController();
String? dropDownValue = 'hello, world'; // add one value as the defaul one which must exists in the dropdown value
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
body: Column(
children: [
Flex(direction: Axis.vertical, children:[
DropdownButton<String>(
value: dropDownValue, // this place should not have a controller but a variable
onChanged: (_value) {
setState(() {
dropDownValue = _value;
});
},
items: ["hello, world", "how are you", "goodbye"]
.map<DropdownMenuItem<String>>((String _value) => DropdownMenuItem<String>(
value: _value, // add this property an pass the _value to it
child: Text(_value,)
)).toList(),
),
])
],
),
);
}
}
please add the VALUE field in both DropdownMenuItem and DropdownButton to prevent error
I have the following custom dropdown widget that I have created.
class CustomDropdown extends StatefulWidget {
final Color? textColor;
final Color? backgroundColor;
final Color? iconColor;
final bool? boldText;
final Object? initialValue;
final List<DropdownMenuItem<Object?>> itemList;
final Function(Object?) onItemSelect;
const CustomDropdown({
Key? key,
this.textColor,
this.backgroundColor,
this.boldText,
this.iconColor,
required this.initialValue,
required this.itemList,
required this.onItemSelect,
}) : super(key: key);
#override
_CustomDropdownState createState() => _CustomDropdownState();
}
class _CustomDropdownState extends State<CustomDropdown> {
late Object? _dropdownValue;
late bool _boldText;
#override
void initState() {
super.initState();
_dropdownValue = widget.initialValue;
_boldText = widget.boldText ?? false;
}
#override
Widget build(BuildContext context) {
return SizedBox(
height: 20,
child: DropdownButtonHideUnderline(
child: DropdownButton<Object?>(
value: _dropdownValue,
icon: Icon(
Icons.expand_more_outlined,
color: widget.iconColor ?? (widget.textColor ?? Colors.black),
),
dropdownColor: widget.backgroundColor ?? Colors.white,
style: TextStyle(
color: widget.textColor ?? Colors.black,
fontWeight: _boldText ? FontWeight.bold : FontWeight.normal,
),
items: widget.itemList,
onChanged: (value) {
setState(() => _dropdownValue = value);
widget.onItemSelect(value);
},
),
),
);
}
}
Then I Instantiate the above widget I have created in a separate dart file as shown below,
class CurrencyDropdown extends StatelessWidget {
const CurrencyDropdown({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Center(
child: CustomDropdown(
initialValue: Currency(id: 1, displayText: 'USD'), <------------ This line throws an error
boldText: true,
iconColor: ColorData.disabledTextColor,
itemList: [
DropdownMenuItem(
child: Text('USD'),
value: Currency(id: 1, displayText: 'USD'),
),
DropdownMenuItem(
child: Text('LKR'),
value: Currency(id: 2, displayText: 'LKR'),
),
],
onItemSelect: (_) {},
),
);
}
}
In the above code, if I replace the value I have pointed with an arrow with null everything works fine. However, if I provide the value shown in the above code snippet, it throws an error.
What the error text says is,
There should be exactly one item with [DropdownButton]'s value: { id: 1, displayText: USD }.
Either zero or 2 or more [DropdownMenuItem]s were detected with the same value
'package:flutter/src/material/dropdown.dart'
Furthermore it shows the following code snippet,
Failed assertion: line 915 pos 15:
'items == null || items.isEmpty || value == null ||
items.where((DropdownMenuItem<T> item) {
return item.value == value;
}).length == 1'
I also came across the below stack overflow posts but could not find a solution,
Flutter Stateful Widgets and Generics
How to implement a dropdown list in flutter?
Flutter dropdown fails when class is provided instead of a string as
value
Can someone please help me? I would really appreciate it!
Your initialValue didn't match any value in itemList, try code below:
class CurrencyDropdown extends StatelessWidget {
CurrencyDropdown({Key? key}) : super(key: key);
List<Currency> list = [
Currency(id: 1, displayText: 'USD'),
Currency(id: 2, displayText: 'LKR'),
];
#override
Widget build(BuildContext context) {
return Center(
child: CustomDropdown(
initialValue: list[0],
boldText: true,
iconColor: Colors.grey,
itemList: list
.map(
(e) => DropdownMenuItem(
child: Text(e.displayText),
value: e,
),
)
.toList(),
onItemSelect: (_) {},
),
);
}
}
I have a list of objects that I can display in a ListView. Now I wanted to implement a search feature and only display the search result. When I try to do it using onChanged on TextField(or even Controller) it doesn't work. I tried to debug and he gets the list updated correctly but he doesn't update the Widget. But when I removed the onChanged and added a button and then called the same method that I was calling on onChanged everything worked.
The goal is to update the widget as the user writes in the text field.
I would be happy to get some help
My full code :
import 'package:flutter/material.dart';
import 'package:hello_fridge/single_ingredient_icon.dart';
import 'package:string_similarity/string_similarity.dart';
import 'entities/ingredient.dart';
class IngredientsContainer extends StatefulWidget {
const IngredientsContainer({Key? key}) : super(key: key);
#override
_IngredientsContainerState createState() => _IngredientsContainerState();
}
class _IngredientsContainerState extends State<IngredientsContainer> {
late List<Ingredient> ingredients;
final searchController = TextEditingController();
#override
void dispose() {
// Clean up the controller when the widget is disposed.
searchController.dispose();
super.dispose();
}
void updateResults(String newValue) {
if (newValue.isEmpty) {
ingredients = Ingredient.getDummyIngredients();
} else {
print("new Value = $newValue");
ingredients = this.ingredients.where((ing) {
double similarity =
StringSimilarity.compareTwoStrings(ing.name, newValue);
print("$similarity for ${ing.name}");
return similarity > 0.2;
}).toList();
ingredients.forEach((element) {
print("found ${element.name}");
});
}
setState(() {});
}
Widget _searchBar(List<Ingredient> ingredients) {
return Row(
children: <Widget>[
IconButton(
splashColor: Colors.grey,
icon: Icon(Icons.restaurant),
onPressed: null,
),
Expanded(
child: TextField(
controller: searchController,
onChanged: (newValue) {
updateResults(newValue);
},
cursorColor: Colors.black,
keyboardType: TextInputType.text,
textInputAction: TextInputAction.go,
decoration: InputDecoration(
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(horizontal: 15),
hintText: "Search..."),
),
),
Padding(
padding: const EdgeInsets.only(right: 8.0),
child: IconButton(
icon: Icon(
Icons.search,
color: Color(0xff9ccc65),
),
onPressed: () {
updateResults(searchController.text);
},
),
),
],
);
}
#override
void initState() {
this.ingredients = Ingredient.getDummyIngredients();
super.initState();
}
#override
Widget build(BuildContext context) {
return Material(
child: Column(children: [
Expanded(flex: 1, child: _searchBar(this.ingredients)),
Expanded(flex: 4, child: IngredientsGrid(this.ingredients))
]),
);
}
}
class IngredientsGrid extends StatelessWidget {
List<Ingredient> ingredients;
IngredientsGrid(this.ingredients);
List<Widget> _buildIngredients() {
return this.ingredients.map((ing) => SingleIngredientIcon(ing)).toList();
}
// const IngredientsGrid({
// Key? key,
// }) : super(key: key);
#override
Widget build(BuildContext context) {
this.ingredients.forEach((ing) => print(ing.name! + ","));
return ListView(
children: <Widget>[
GridView.count(
crossAxisCount: 4,
// physics: NeverScrollableScrollPhysics(),
// to disable GridView's scrolling
shrinkWrap: true,
// You won't see infinite size error
children: _buildIngredients()),
// ...... other list children.
],
);
}
}
Moreover, I keep getting this Warning :
"Changing the content within the composing region may cause the input method to behave strangely, and is therefore discouraged. See https://github.com/flutter/flutter/issues/78827 for more details".
Visiting the linked GitHub page wasn't helpful
The problem is that while you are correctly filtering the list but your TextController is not getting assigned any value.
So, no value is getting assigned to your TextField as the initial value and hence the list again filters to have the entire list.
To solve this just assign the TextController the newValue like this.
void updateResults(String newValue) {
if (newValue.isEmpty) {
ingredients = Ingredient.getDummyIngredients();
} else {
print("new Value = $newValue");
ingredients = this.ingredients.where((ing) {
double similarity =
StringSimilarity.compareTwoStrings(ing.name, newValue);
print("$similarity for ${ing.name}");
return similarity > 0.2;
}).toList();
ingredients.forEach((element) {
print("found ${element.name}");
});
}
// change
searchController = TextEditingController.fromValue(
TextEditingValue(
text: newValue,
),
);
setState(() {});
}
If it throws an error then remove final from the variable declaration, like this :
var searchController = TextEditingController();
I try to create a dropdown and to populate it with few objects which represents few servers from where the user can pick one, but when I run the app I'm getting an error saying:
The following assertion was thrown building DropdownWidget(dirty, state: _DropdownWidgetState#1f58f): There should be exactly one item with [DropdownButton]'s value: Instance of 'ServerModel'. Either zero or 2 or more [DropdownMenuItem]s were detected with the same value
Can you please help me to identify what I'm doing wrong in my code?
import 'package:flutter/material.dart';
class ServerSettingsPage extends StatefulWidget {
#override
_ServerSettingsPageState createState() => _ServerSettingsPageState();
}
class _ServerSettingsPageState extends State<ServerSettingsPage> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Server Settings")),
body: _buildUI(),
);
}
Widget _buildUI() {
return Padding(
padding: const EdgeInsets.fromLTRB(0, 20, 0, 0),
child: Center(
child: Column(
children: <Widget>[
Text(
'Select a server:',
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
),
DropdownWidget(),
],
),
),
);
}
}
class DropdownWidget extends StatefulWidget {
DropdownWidget({Key key}) : super(key: key);
#override
_DropdownWidgetState createState() => _DropdownWidgetState();
}
class _DropdownWidgetState extends State<DropdownWidget> {
ServerModel dropdownValue =
ServerModel(name: 'Default', url: 'https://defaultServer.com/');
#override
Widget build(BuildContext context) {
return DropdownButton<ServerModel>(
value: dropdownValue,
icon: Icon(Icons.arrow_downward),
iconSize: 24,
elevation: 16,
style: TextStyle(color: Colors.purple[700]),
underline: Container(
height: 2,
color: Colors.purple[700],
),
onChanged: (ServerModel newServer) {
setState(() {
dropdownValue = newServer;
});
},
items: <ServerModel>[
ServerModel(name: 'Default', url: 'https:defaultServer.com/'),
ServerModel(name: 'Alpha', url: 'https://alphaServer.com/'),
ServerModel(name: 'Beta', url: 'https://betaServer.com/'),
].map<DropdownMenuItem<ServerModel>>((ServerModel server) {
return DropdownMenuItem<ServerModel>(
value: server,
child: Text(server.name, style: TextStyle(fontSize: 20)),
);
}).toList(),
);
}
}
And here is the ServerModel class:
class ServerModel {
ServerModel({this.name, this.url});
ServerModel.empty() {
this.name = null;
this.url = null;
}
String name;
String url;
}
Many thanks for reading this post.
There should be exactly one item with [DropdownButton]'s value:
Instance of 'ServerModel'. Either zero or 2 or more
[DropdownMenuItem]s were detected with the same value
This is happening because selected value inside the dropdown has to point to an existing list item (and obviously there shouldn't be any duplicates in that list). The way you've set it up right now is that the list of ServerModel is being generated during your widget build time and once it is built there no reference to the list inside the state of the widget.
I hope my answer is clear enough, also take a look at correct code bellow:
class _DropdownWidgetState extends State<DropdownWidget> {
List<ServerModel> serverModels = <ServerModel>[
ServerModel(name: 'Default', url: 'https:defaultServer.com/'),
ServerModel(name: 'Alpha', url: 'https://alphaServer.com/'),
ServerModel(name: 'Beta', url: 'https://betaServer.com/'),
];
ServerModel selectedServer;
#override
initState() {
super.initState();
selectedServer = serverModels[0];
}
#override
Widget build(BuildContext context) {
return DropdownButton<ServerModel>(
value: selectedServer,
icon: Icon(Icons.arrow_downward),
iconSize: 24,
elevation: 16,
style: TextStyle(color: Colors.purple[700]),
underline: Container(
height: 2,
color: Colors.purple[700],
),
onChanged: (ServerModel newServer) {
setState(() {
selectedServer = newServer;
});
},
items: serverModels.map((ServerModel map) {
return new DropdownMenuItem<ServerModel>(
value: map, child: Text(map.name));
}).toList(),
);
}
}
Tested, working interactive answer on dartpad:
https://dartpad.dev/153bad9baac64382e27bc41cdc8131c9
You're facing an equality problem.
In Dart, non-primitive types like SizedBox, List, and in your case, ServerModel are compared to each other using referential equality, meaning that they are equal to each other if they have the same reference. That is, they are the same instance.
So this code will print false:
print(ServerModel(name: 'Default', url: 'https://defaultServer.com/') == ServerModel(name: 'Default', url: 'https://defaultServer.com/'));
// TL;DR
print(ServerModel(xyz) == ServerModel(xyz)); // false
The solution would be to override the equality operator for your class ServerModel.
class ServerModel {
ServerModel({this.name, this.url});
ServerModel.empty() {
this.name = null;
this.url = null;
}
String name;
String url;
#override
bool operator ==(Object other) {
if (identical(this, other)) return true;
return other is ServerModel && other.name == name && other.url == url;
}
#override
int get hashCode => name.hashCode ^ url.hashCode;
}
Now it should work.
PRO TIP: Use equatable to automatically generate equality and hashcode.