how to make the DropdownMenuEntries occupy the full width, flutter? - flutter

I have a DropdownMenu and I have set that it occupies the full width of the screen, but their DropdownMenuEntries do not the same. I have tried with the ButtonStyle widget, but it does not work.
I attach a screenshot
I have pointed with a blue oval the unfilled space of the menu
The code is
class DropDownMenuCustom extends StatefulWidget {
final List<String> items;
final String? value;
final String? hintText;
final String? label;
final void Function(String?)? onSelected;
final TextEditingController? controller;
const DropDownMenuCustom({
Key? key,
required this.items,
this.value,
this.hintText,
required this.label,
this.onSelected,
this.controller,
}) : super(key: key);
#override
State<DropDownMenuCustom> createState() => _DropDownMenuCustomState();
}
class _DropDownMenuCustomState extends State<DropDownMenuCustom> {
late TextEditingController menuController;
String? selectedValue;
#override
void initState() {
selectedValue = widget.value;
menuController = TextEditingController(text: selectedValue);
super.initState();
}
_handleOnSelected(String? newVal) {
widget.onSelected!(newVal);
}
#override
void dispose() {
menuController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
var _width = MediaQuery.of(context).size.width;
log(_width.toString());
return DropdownMenu<String>(
width: _width,
controller: menuController,
enableSearch: false,
hintText: widget.hintText,
menuStyle: MenuStyle(
backgroundColor: MaterialStateProperty.all<Color>(Colors.red)),
dropdownMenuEntries: widget.items
.map(
(e) => DropdownMenuEntry<String>(
value: e,
label: e,
),
)
.toList(),
inputDecorationTheme: _decoration(),
onSelected: widget.onSelected != null ? _handleOnSelected : null,
);
}
}
Information:
Flutter 3.7.0
dart 2.19.0

Related

Flutter: Undefined name 'uid'

class Rapidoreach extends StatefulWidget {
const Rapidoreach({
Key? key,
this.width,
this.height,
this.uid,
}) : super(key: key);
final double? width;
final double? height;
final String? uid;
#override
_RapidoreachState createState() => _RapidoreachState();
}
class _RapidoreachState extends State<Rapidoreach> {
#override
void initState() {
RapidoReach.instance.init(apiToken: 'api-key', userId: 'userid');
RapidoReach.instance.setOnRewardListener(onRapidoReachReward);
RapidoReach.instance.setRewardCenterClosed(onRapidoReachRewardCenterClosed);
RapidoReach.instance.setRewardCenterOpened(onRapidoReachRewardCenterOpened);
RapidoReach.instance
.setSurveyAvaiableListener(onRapidoReachSurveyAvailable);
super.initState();
}
void onRapidoReachReward(num quantity) {
final DocumentReference docRef = FirebaseFirestore.instance.collection("users").doc(uid); //Here is the error (uid)
docRef.update({"prizecoins": FieldValue.increment(quantity)});
}
void onRapidoReachSurveyAvailable(int? survey) {
debugPrint('ROR: $survey');
}
void onRapidoReachRewardCenterClosed() {
debugPrint('ROR: closed');
}
void onRapidoReachRewardCenterOpened() {
debugPrint('ROR: opened');
}
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
ElevatedButton(
child: const Text("Launch RapidoReach"),
onPressed: () => RapidoReach.instance.show(),
),
],
)),
),
);
}
}
Why do I have an issue saying that uid is not defined when is already defined in StatefulWidget?
Can someone help me?
In the state of a StatefulWidget, you can access the instance of the widget through the getter widget, so you need to replace uid with widget.uid:
void onRapidoReachReward(num quantity) {
final DocumentReference docRef =
FirebaseFirestore.instance.collection("users").doc(widget.uid);
docRef.update({"prizecoins": FieldValue.increment(quantity)});
}
you can get the properties from the StatefulWidget inside the State Object by assigning the widget keyword at first :
widget.uid
in your case replace with this :
FirebaseFirestore.instance.collection("users").doc(widget.uid);

type '(String) => void' is not a subtype of type '(dynamic) => void'

This is where I use the dropdown:
class _JobFunctionState extends State<JobFunction> {
static const jobList = <String>["Item 1", "Item 2", "Item 3"];
String dropdownValue = jobList[0];
#override
Widget build(BuildContext context) {
return Dropdown<String>(
hint: const Text("Choose job function"),
labelText: "Job Function",
value: dropdownValue,
dropdownMenuItemList: jobList
.map<DropdownMenuItem<String>>(
(String job) => DropdownMenuItem<String>(
value: job,
child: Text(job),
))
.toList(),
onChanged: (newDropdownValue) {
setState(() {
dropdownValue = newDropdownValue;
});
},
);
}
Here's the full dropdown class:
class Dropdown<T> extends StatefulWidget {
final List<DropdownMenuItem<T>> dropdownMenuItemList;
final ValueChanged<T> onChanged;
final T value;
final bool isBorder;
final double radius;
final TextStyle? textStyle;
final Color? color;
final Widget hint;
final String labelText;
const Dropdown(
{Key? key,
required this.dropdownMenuItemList,
required this.onChanged,
required this.value,
this.isBorder = true,
this.radius = 10.0,
this.textStyle,
this.color,
required this.hint,
required this.labelText})
: super(key: key);
_DropdownState<T> createState() => _DropdownState();
}
class _DropdownState<T> extends State<Dropdown> {
#override
Widget build(BuildContext context) {
return FormField<T>(
builder: (FormFieldState<T> state) {
return SingleChildScrollView(
child: DropdownButtonFormField<T>(
isExpanded: true,
itemHeight: 50.0,
items: widget.dropdownMenuItemList as List<DropdownMenuItem<T>>,
onChanged: widget.onChanged,
value: widget.value,
dropdownColor: Colors.white,
iconEnabledColor: Colors.grey,
icon: const Icon(Icons.arrow_drop_down),
hint: widget.hint,
),
);
},
);
}
}
and ValueChanged is:
typedef ValueChanged<T> = void Function(T value);
Dropdown is generic, I've made everything string in the jobFunction widget, and I get this error.
if I add dynamic to onChanged parameter(), but the type of the parameter should not be dynamic, but String. Any ideas?
adding text to fill the requirements for edit: slkdjf s;lkdjfsd jfiosdj fsdnf lksdjf klsjdfi skjldfj slkdj flksdjlkifj sf kjsdlk;fj slk;dj fisjd fiosj f;ajof hsiod jfsajfkl sjd fk jsdlf sdlkf lksjdfoijsfoi jsdlkjf lksadj flksdjflk sjdalkf jsakj fjsaoif jseij flisd jflksajflk jasdlk
I got the same issue
and what did work for me was replacing :
class _DropdownState<T> extends State<Dropdown>
by this:
class _DropdownState<T> extends State<Dropdown<T>>
Remember also to change the overrided createState method:
#override
_BasicDropdownState<T> createState() => _BasicDropdownState<T>();
Dropdown is generic, I've made everything string in the jobFunction
widget, and I get this error.
ValueChanged should be nullable value. If you specific to a String , then the onchange will specific to String? : means String or null.
Because if there is no changes, it must be null.
how to assign the String? to your variable that non-null String is by add a condition like below.
onChanged: (newDropdownValue) {
if( newDropdownValue != null){
setState(() {
dropdownValue = newDropdownValue!;
});
}
},
if no changes happend, then you dont need to set new value to you dropdownValue
hope this solve your issue.

Can Anyone solve Custom DropDownButton issue. Flutter ( i know many will not be able to )

TO SOLVE THIS U NEED TO RUN IT FIRST
Only for them who have experience with DropDownButton and T typepassing can solve this.
Please Help!
import 'package:flutter/material.dart';
// ignore: must_be_immutable
class SupDropDownButton<T> extends StatefulWidget {
FormFieldValidator<T>? validator;
ValueChanged<T> value;
final List<T> data;
SupDropDownButton(
{Key? key, required this.data, this.validator, required this.value})
: super(key: key);
#override
State<SupDropDownButton> createState() => _SupDropDownButtonState<T>();
}
class _SupDropDownButtonState<T> extends State<SupDropDownButton> {
T? _value;
List<DropdownMenuItem<T>> items() =>
widget.data.cast<T>().map<DropdownMenuItem<T>>(menuItem).toList();
DropdownMenuItem<T> menuItem(dynamic value) => DropdownMenuItem<T>(
value: value,
child: Text(value.name),
);
#override
Widget build(BuildContext context) {
return DropdownButtonFormField<T>(
decoration: const InputDecoration(border: InputBorder.none),
validator: widget.validator,
value: _value,
onChanged: (T? val) {
FocusScope.of(context).requestFocus(FocusNode());
_value = val!;
widget.value.call(val);
setState(() {});
},
items: items(),
hint: const Text('Please select Categories'),
);
}
}
THIS IS THE ERROR
Expected a value of type ((dynamic) => String?)?, but got one of type (Employee) => String?
I have worked on your code. Instead of value.name in your code, I have directly add List<String> for easy reference and it's working fine.I using null safety, that's why add late initialize to data list.if you need to create object and insert JSON data means revert back will rework on it
static data added like List<String> employeeList = ['hari','chetanPatil'] and cast it to data;
Working code :
// ignore: must_be_immutable
class SupDropDownButton<T> extends StatefulWidget {
FormFieldValidator<T>? validator;
ValueChanged<T> value;
late List<T> data;
SupDropDownButton(
{Key? key, required this.data, this.validator, required this.value})
: super(key: key);
#override
State<SupDropDownButton> createState() => _SupDropDownButtonState<T>();
}
class _SupDropDownButtonState<T> extends State<SupDropDownButton> {
T? _value;
List<String> employeeList = ['hari', 'chetanPatil'];
bool isOnLoad = true;
#override
void initState() {
super.initState();
}
List<DropdownMenuItem<T>> items() =>
widget.data.cast<T>().map<DropdownMenuItem<T>>(menuItem).toList();
DropdownMenuItem<T> menuItem(dynamic value) => DropdownMenuItem<T>(
value: value,
child: Text(value),
);
#override
Widget build(BuildContext context) {
widget.data.clear();
widget.data.addAll(employeeList);
return Scaffold(
body: Center(
child: Padding(
padding: const EdgeInsets.all(40.0),
child: DropdownButtonFormField<T>(
decoration: const InputDecoration(border: InputBorder.none),
validator: widget.validator,
value: _value,
onChanged: (T? val) {
FocusScope.of(context).requestFocus(FocusNode());
_value = val!;
print(_value);
widget.value.call(val);
setState(() {});
},
items: items(),
hint: const Text('Please select Categories'),
),
),
),
);
}
}

Can I override MaterialBanner widget to change its height?

I was dealing with the ScaffoldMessenger.of(context).showMaterialBanner method and it needs a MaterialBanner widget as parameter.
Goal
In my case I need to put an empty MaterialBanner widget but with a custom height (less than default).
In few words override the original MaterialBanner build method.
What I tried
so I thought that I had to extend a StatefulWidget implementing MaterialBanner:
class MiniBanner extends StatefulWidget implements MaterialBanner {
const MiniBanner({Key? key}) : super(key: key);
#override
List<Widget> get actions => [const SizedBox()];
#override
Animation<double>? get animation => null;
#override
Color? get backgroundColor => Colors.amber;
#override
Widget get content => const SizedBox();
#override
TextStyle? get contentTextStyle => null;
#override
double? get elevation => 0;
#override
bool get forceActionsBelow => false;
#override
Widget? get leading => null;
#override
EdgeInsetsGeometry? get leadingPadding => EdgeInsets.zero;
#override
VoidCallback? get onVisible => null;
#override
OverflowBarAlignment get overflowAlignment => OverflowBarAlignment.end;
#override
EdgeInsetsGeometry? get padding => EdgeInsets.zero;
#override
MaterialBanner withAnimation(Animation<double> newAnimation, {Key? fallbackKey}) {
return MaterialBanner(
key: key ?? fallbackKey,
content: content,
contentTextStyle: contentTextStyle,
actions: actions,
elevation: elevation,
leading: leading,
backgroundColor: backgroundColor,
padding: padding,
leadingPadding: leadingPadding,
forceActionsBelow: forceActionsBelow,
overflowAlignment: overflowAlignment,
animation: newAnimation,
onVisible: onVisible,
);
}
#override
State<MiniBanner> createState() => _MiniBannerState();
}
class _MiniBannerState extends State<MiniBanner> {
#override
Widget build(BuildContext context) {
// I want the code to go here to build a smaller MaterialBanner
return Container(
color: widget.backgroundColor,
height: 10,
);
}
}
But my code keep going to the original MaterialBanner method.
Maybe I'm missing some knowledge about extending stateful widgets.
Solved
The main issue was the return type of the "withAnimation" method. It has to be the same type of your custom widget.
So:
class MiniBanner extends StatefulWidget implements MaterialBanner {
const MiniBanner({
Key key,
this.content = const SizedBox(),
this.contentTextStyle,
this.actions = const [SizedBox()],
this.elevation = 0,
this.leading,
this.backgroundColor = Colors.red, //custom default color
this.padding = EdgeInsets.zero,
this.leadingPadding = EdgeInsets.zero,
this.forceActionsBelow = false,
this.overflowAlignment = OverflowBarAlignment.end,
this.animation,
this.onVisible,
}) : assert(elevation == null || elevation >= 0.0),
assert(content != null),
assert(actions != null),
assert(forceActionsBelow != null),
super(key: key);
#override
final List<Widget> actions;
#override
final Animation<double> animation;
#override
final Color backgroundColor;
#override
final Widget content;
#override
final TextStyle contentTextStyle;
#override
final double elevation;
#override
final bool forceActionsBelow;
#override
final Widget leading;
#override
final EdgeInsetsGeometry leadingPadding;
#override
final VoidCallback onVisible;
#override
final OverflowBarAlignment overflowAlignment;
#override
final EdgeInsetsGeometry padding;
#override
MiniBanner withAnimation(Animation<double> newAnimation, {Key fallbackKey}) {
return MiniBanner(
key: key ?? fallbackKey,
content: content,
contentTextStyle: contentTextStyle,
actions: actions,
elevation: elevation,
leading: leading,
backgroundColor: backgroundColor,
padding: padding,
leadingPadding: leadingPadding,
forceActionsBelow: forceActionsBelow,
overflowAlignment: overflowAlignment,
animation: newAnimation,
onVisible: onVisible,
);
}
#override
State<MiniBanner> createState() => MiniBannerState();
}
class MiniBannerState extends State<MiniBanner> {
#override
Widget build(BuildContext context) {
// Now our MiniBanner correctly build this method
return Container(
color: widget.backgroundColor,
height: 10,
);
}
}
Important note
This code is not null sound safety so if you have a Flutter application with null sound safety enabled you have to change some syntax, like adding ? after the nullable properties and so on.
UPDATE
If you want to really emulate the original MaterialBanner with all the animations, you have to copy almost the entire original "build" method.
Then, to custom your widget, you just have to edit the assignment of materialBanner variable, example:
// other parts of code copied from original build method above
Widget materialBanner = Container(
color: widget.backgroundColor,
height: 10,
);
// other parts of code copied from original build method below

Why is my Checkbox not updating when I pass a callback from a stateful widget to stateless widget?

Im trying to use Lift the State for my Checkbox and I know that by changing my TaskTile to a stateful widget ill be able to solve my problem but I want to know why my Checkbox isnt updating when I pass the setstate as a callback from a stateful widget.Im sorry if this question is dumb im just getting started with Flutter so any help is appreciated.
This is my stateless widget which builds the checkbox in a tile
import 'package:flutter/material.dart';
class TaskTile extends StatelessWidget {
final String title;
final bool isChecked;
final void Function(bool?) checkboxCallback;
TaskTile({required this.title,required this.isChecked,required this.checkboxCallback});
#override
Widget build(BuildContext context) {
return ListTile(
title: Text(title,style: TextStyle(decoration: isChecked?TextDecoration.lineThrough:null),),
trailing: Checkbox(
value: isChecked,
onChanged: checkboxCallback,
),
);
}
}
And this is the stateful widget where I am giving the callback
import 'package:flutter/material.dart';
import 'task_tile.dart';
import 'task.dart';
class TaskList extends StatefulWidget {
#override
_TaskListState createState() => _TaskListState();
}
class _TaskListState extends State<TaskList> {
#override
Widget build(BuildContext context) {
bool isChecked = false;
List<Task> tasks = [
Task(title: 'Buy Milk', isDone: isChecked),
Task(title: 'Buy Bread', isDone: isChecked),
Task(title: 'Buy Eggs', isDone: isChecked),
];
return ListView.builder(itemBuilder: (context, index) {
return TaskTile(title: tasks[index].title,isChecked: tasks[index].isDone,checkboxCallback: (checkboxState){
setState(() {
tasks[index].toggleIsDone();
});
},);
},itemCount: tasks.length,);
}
}
Also this is the Task Class where I am giving the toggle function to change the state
class Task{
final String title;
bool isDone;
Task({required this.title,required this.isDone});
void toggleIsDone(){
isDone = !isDone;
}
}
You are not calling the function.
Instead of
trailing: Checkbox(
value: isChecked,
onChanged: checkboxCallback,
),
Can you try
trailing: Checkbox(
value: isChecked,
onChanged: (){checkboxCallback();},
),
You need to make TaskTile statefull. TaskList can be StatelessWidget.
Here is the answer:
import 'package:flutter/material.dart';
class TaskList extends StatelessWidget {
#override
Widget build(BuildContext context) {
bool isChecked = false;
List<Task> tasks = [
Task(title: 'Buy Milk', isDone: isChecked),
Task(title: 'Buy Bread', isDone: isChecked),
Task(title: 'Buy Eggs', isDone: isChecked),
];
return ListView.builder(
itemBuilder: (context, index) {
return TaskTile(
title: tasks[index].title,
isChecked: tasks[index].isDone,
checkboxCallback: (value) {
tasks[index].toggleIsDone();
},
);
},
itemCount: tasks.length,
);
}
}
class TaskTile extends StatefulWidget {
final String title;
final bool isChecked;
final Function checkboxCallback;
TaskTile(
{required this.title,
required this.isChecked,
required this.checkboxCallback,
Key? key})
: super(key: key);
#override
_TaskTileState createState() => _TaskTileState();
}
class _TaskTileState extends State<TaskTile> {
late bool _isChecked;
#override
void initState() {
super.initState();
_isChecked = widget.isChecked;
}
#override
Widget build(BuildContext context) {
return ListTile(
title: Text(
widget.title,
style: TextStyle(
decoration: _isChecked ? TextDecoration.lineThrough : null),
),
trailing: Checkbox(
value: _isChecked,
onChanged: (value) {
widget.checkboxCallback(value);
setState(() {
_isChecked = value!;
});
},
),
);
}
}
class Task {
final String title;
bool isDone;
Task({required this.title, required this.isDone});
void toggleIsDone() {
isDone = !isDone;
}
}
For any dynamic change your widget class need to be stateful, if you are using vscode than you can easily change from stateless to statful . First hover to your statless widget and than you will get a bulb like indicator , press it than you will get an option of changing the state.
According to my understanding, You are trying to change the state (isChanged) of the Task object in a list. This list inturn is used in listview builder. Since flutter work with immutable data and you are sending the reference of the list to the listview builder, the changes of the element in the list wont be reflected in the UI, eventhough you used setState function. Instead try creating a new list with the same name with the updated element. This will definitely work.
I got the idea from this solution - https://stackoverflow.com/a/61847327/11844877
PS
I am a newbie to flutter and don't mind if my answer wasn't helpful.
This is the solution from the link below:
import 'package:flutter/material.dart';
class TaskList extends StatelessWidget {
#override
Widget build(BuildContext context) {
bool isChecked = false;
List<Task> tasks = [
Task(title: 'Buy Milk', isDone: isChecked),
Task(title: 'Buy Bread', isDone: isChecked),
Task(title: 'Buy Eggs', isDone: isChecked),
];
return ListView.builder(
itemBuilder: (context, index) {
return TaskTile(
title: tasks[index].title,
isChecked: tasks[index].isDone,
checkboxCallback: (value) {
tasks[index].toggleIsDone();
},
);
},
itemCount: tasks.length,
);
}
}
class TaskTile extends StatefulWidget {
final String title;
final bool isChecked;
final Function checkboxCallback;
TaskTile(
{required this.title,
required this.isChecked,
required this.checkboxCallback,
Key? key})
: super(key: key);
#override
_TaskTileState createState() => _TaskTileState();
}
class _TaskTileState extends State<TaskTile> {
late bool _isChecked;
#override
void initState() {
super.initState();
_isChecked = widget.isChecked;
}
#override
Widget build(BuildContext context) {
return ListTile(
title: Text(
widget.title,
style: TextStyle(
decoration: _isChecked ? TextDecoration.lineThrough : null),
),
trailing: Checkbox(
value: _isChecked,
onChanged: (value) {
widget.checkboxCallback(value);
setState(() {
_isChecked = value!;
});
},
),
);
}
}
class Task {
final String title;
bool isDone;
Task({required this.title, required this.isDone});
void toggleIsDone() {
isDone = !isDone;
}
}
https://codeutility.org/why-is-my-checkbox-not-updating-when-i-pass-a-callback-from-a-stateful-widget-to-stateless-widget/