How can I create a custom radio button group like this in flutter
Here is the full code
class CustomRadio extends StatefulWidget {
#override
createState() {
return new CustomRadioState();
}
}
class CustomRadioState extends State<CustomRadio> {
List<RadioModel> sampleData = new List<RadioModel>();
#override
void initState() {
// TODO: implement initState
super.initState();
sampleData.add(new RadioModel(false, 'A', 'April 18'));
sampleData.add(new RadioModel(false, 'B', 'April 17'));
sampleData.add(new RadioModel(false, 'C', 'April 16'));
sampleData.add(new RadioModel(false, 'D', 'April 15'));
}
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text("ListItem"),
),
body: new ListView.builder(
itemCount: sampleData.length,
itemBuilder: (BuildContext context, int index) {
return new InkWell(
//highlightColor: Colors.red,
splashColor: Colors.blueAccent,
onTap: () {
setState(() {
sampleData.forEach((element) => element.isSelected = false);
sampleData[index].isSelected = true;
});
},
child: new RadioItem(sampleData[index]),
);
},
),
);
}
}
class RadioItem extends StatelessWidget {
final RadioModel _item;
RadioItem(this._item);
#override
Widget build(BuildContext context) {
return new Container(
margin: new EdgeInsets.all(15.0),
child: new Row(
mainAxisSize: MainAxisSize.max,
children: <Widget>[
new Container(
height: 50.0,
width: 50.0,
child: new Center(
child: new Text(_item.buttonText,
style: new TextStyle(
color:
_item.isSelected ? Colors.white : Colors.black,
//fontWeight: FontWeight.bold,
fontSize: 18.0)),
),
decoration: new BoxDecoration(
color: _item.isSelected
? Colors.blueAccent
: Colors.transparent,
border: new Border.all(
width: 1.0,
color: _item.isSelected
? Colors.blueAccent
: Colors.grey),
borderRadius: const BorderRadius.all(const Radius.circular(2.0)),
),
),
new Container(
margin: new EdgeInsets.only(left: 10.0),
child: new Text(_item.text),
)
],
),
);
}
}
class RadioModel {
bool isSelected;
final String buttonText;
final String text;
RadioModel(this.isSelected, this.buttonText, this.text);
}
To use :
void main() {
runApp(new MaterialApp(
home: new CustomRadio(),
));
}
Screenshot :
Screenshot (Null safe)
Full code:
Create this custom class.
class MyRadioListTile<T> extends StatelessWidget {
final T value;
final T groupValue;
final String leading;
final Widget? title;
final ValueChanged<T?> onChanged;
const MyRadioListTile({
required this.value,
required this.groupValue,
required this.onChanged,
required this.leading,
this.title,
});
#override
Widget build(BuildContext context) {
final title = this.title;
return InkWell(
onTap: () => onChanged(value),
child: Container(
height: 56,
padding: EdgeInsets.symmetric(horizontal: 16),
child: Row(
children: [
_customRadioButton,
SizedBox(width: 12),
if (title != null) title,
],
),
),
);
}
Widget get _customRadioButton {
final isSelected = value == groupValue;
return Container(
padding: EdgeInsets.symmetric(horizontal: 12, vertical: 8),
decoration: BoxDecoration(
color: isSelected ? Colors.blue : null,
borderRadius: BorderRadius.circular(4),
border: Border.all(
color: isSelected ? Colors.blue : Colors.grey[300]!,
width: 2,
),
),
child: Text(
leading,
style: TextStyle(
color: isSelected ? Colors.white : Colors.grey[600]!,
fontWeight: FontWeight.bold,
fontSize: 18,
),
),
);
}
}
Use it in your widget like a regular RadioListTile.
class _MyPageState extends State<MyPage> {
int _value = 1;
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: [
MyRadioListTile<int>(
value: 1,
groupValue: _value,
leading: 'A',
title: Text('One'),
onChanged: (value) => setState(() => _value = value!),
),
MyRadioListTile<int>(
value: 2,
groupValue: _value,
leading: 'B',
title: Text('Two'),
onChanged: (value) => setState(() => _value = value!),
),
MyRadioListTile<int>(
value: 3,
groupValue: _value,
leading: 'C',
title: Text('Three'),
onChanged: (value) => setState(() => _value = value!),
),
],
),
);
}
}
I achieved that with the following logic.
reply if you need a detailed explanation
import 'package:flutter/material.dart';
class Parent extends StatefulWidget {
Parent({
Key key,
}) : super(key: key);
#override
_ParentState createState() => _ParentState();
}
class _ParentState extends State<Parent> {
int _selectedItem = 0;
selectItem(index) {
setState(() {
_selectedItem = index;
print(selectItem.toString());
});
}
#override
Widget build(BuildContext context) {
//...YOUR WIDGET TREE HERE
return ListView.builder(
shrinkWrap: true,
itemCount: 5,
itemBuilder: (context, index) {
return CustomItem(
selectItem, // callback function, setstate for parent
index: index,
isSelected: _selectedItem == index,
title: index.toString(),
);
},
);
}
}
class CustomItem extends StatefulWidget {
final String title;
final int index;
final bool isSelected;
Function(int) selectItem;
CustomItem(
this.selectItem, {
Key key,
this.title,
this.index,
this.isSelected,
}) : super(key: key);
_CustomItemState createState() => _CustomItemState();
}
class _CustomItemState extends State<CustomItem> {
#override
Widget build(BuildContext context) {
return Row(
children: <Widget>[
Text("${widget.isSelected ? "true" : "false"}"),
RaisedButton(
onPressed: () {
widget.selectItem(widget.index);
},
child: Text("${widget.title}"),
)
],
);
}
}
You can create it with ListView and List Item with one local variable to store the selected item. And you can render the selected the ListItem based on the variable.
P.S. Let me know if you need code snippet.
[EDIT]
As you have requested, Here is code snipper which will show you how you can maintain the state of each ListView item.
Now you can play with it and make it the way you want. If you want only one selected item you can write the logic that way.
void main() {
runApp(new MaterialApp(
home: new ListItemDemo(),
));
}
class ListItemDemo extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text("ListItem"),
),
body: new ListView.builder(
itemCount: 10,
itemBuilder: (BuildContext context, int index) {
return new MyListItem(
title: "Hello ${index + 1}",
);
}),
);
}
}
class MyListItem extends StatefulWidget {
final String title;
MyListItem({this.title});
#override
_MyListItemState createState() => new _MyListItemState();
}
class _MyListItemState extends State<MyListItem> {
bool isSelected;
#override
void initState() {
super.initState();
isSelected = false;
}
#override
Widget build(BuildContext context) {
return new Row(
children: <Widget>[
new Text("${widget.title} ${isSelected ? "true" : "false"}"),
new RaisedButton(
onPressed: () {
if (isSelected) {
setState(() {
isSelected = false;
});
} else {
setState(() {
isSelected = true;
});
}
},
child: new Text("Select"),
)
],
);
}
}
https://i.stack.imgur.com/Hq0O2.png
Here is Custom Radio Group Button Widget. You can easily customize all property as per your requirement. Use:
GroupRadioButton(
label: [Text("A"), Text("B"), Text("C"), Text("D")],
padding: EdgeInsets.symmetric(vertical: 10),
spaceBetween: 5,
radioRadius: 10,
color: Const.mainColor,
onChanged: (listIndex) {
print(listIndex);
},
),
This is GroupRadioButton widget
import 'package:flutter/material.dart';
class GroupRadioButton extends StatefulWidget {
GroupRadioButton({
#required this.label,
#required this.padding,
#required this.onChanged,
this.color = Colors.blue,
this.radioRadius = 14.0,
this.spaceBetween = 5.0,
this.mainAxisAlignment = MainAxisAlignment.start,
this.crossAxisAlignment = CrossAxisAlignment.start,
});
final Color color;
final List<Widget> label;
final EdgeInsets padding;
final Function(int) onChanged;
final double radioRadius;
final double spaceBetween;
final MainAxisAlignment mainAxisAlignment;
final CrossAxisAlignment crossAxisAlignment;
#override
_GroupRadioButtonState createState() => _GroupRadioButtonState();
}
class _GroupRadioButtonState extends State<GroupRadioButton> {
int selectedIndex = 0;
#override
Widget build(BuildContext context) {
return ListView.builder(
shrinkWrap: true,
itemCount: widget.label != null ? widget.label.length : 0,
itemBuilder: (context, index) {
return LabeledRadio(
selectedIndex: selectedIndex,
color: widget.color,
onChanged: (value) {
setState(() {
selectedIndex = value;
widget.onChanged(value);
// print(value);
});
},
index: index,
label: widget.label[index],
crossAxisAlignment: widget.crossAxisAlignment,
mainAxisAlignment: widget.mainAxisAlignment,
radioRadius: widget.radioRadius,
spaceBetween: widget.spaceBetween,
padding: widget.padding,
);
});
}
}
class LabeledRadio extends StatelessWidget {
LabeledRadio({
#required this.label,
#required this.index,
#required this.color,
//#required this.groupValue,
//#required this.value,
#required this.onChanged,
#required this.radioRadius,
#required this.padding,
#required this.spaceBetween,
#required this.mainAxisAlignment,
#required this.crossAxisAlignment,
this.selectedIndex,
});
final Color color;
final int selectedIndex;
final Widget label;
final index;
final EdgeInsets padding;
//final bool groupValue;
//final bool value;
final Function(int) onChanged;
final double radioRadius;
final double spaceBetween;
final MainAxisAlignment mainAxisAlignment;
final CrossAxisAlignment crossAxisAlignment;
#override
Widget build(BuildContext context) {
return InkWell(
onTap: () {
onChanged(index);
},
child: Padding(
padding: padding,
child: Row(
mainAxisAlignment: mainAxisAlignment,
crossAxisAlignment: crossAxisAlignment,
children: <Widget>[
Container(
decoration: BoxDecoration(
//color: Const.mainColor,
shape: BoxShape.circle,
border: Border.all(color: color, width: 2),
),
padding: EdgeInsets.all(2),
child: selectedIndex == index
? Container(
height: radioRadius,
width: radioRadius,
decoration: BoxDecoration(
color: color,
shape: BoxShape.circle,
),
)
: Container(
height: radioRadius,
width: radioRadius,
),
),
SizedBox(
width: spaceBetween,
),
label,
],
),
),
);
}
}
My RadioButton is like the 'Radio' widget:
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
class RadioButton<T> extends StatefulWidget {
RadioButton({
Key key,
#required this.value,
#required this.caption,
#required this.groupValue,
#required this.onChanged,
}) : assert(value != null),
assert(caption != null),
assert(groupValue != null),
assert(onChanged != null),
super(key: key);
final T value;
final T groupValue;
final String caption;
final Function onChanged;
#override
State<StatefulWidget> createState() => _RadioButtonState();
}
class _RadioButtonState extends State<RadioButton> {
#override
Widget build(BuildContext context) {
final bool selected = widget.value == widget.groupValue;
return GestureDetector(
onTap: () {
widget.onChanged(widget.value);
},
child: Container(
width: double.maxFinite,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8),
color: selected ? Colors.red : Colors.white),
child: Padding(
padding: const EdgeInsets.all(16),
child: Text(
widget.caption,
textAlign: TextAlign.center,
style: Theme.of(context)
.textTheme
.button
.copyWith(color: selected ? Colors.white : Colors.red),
),
),
),
);
}
}
import 'package:flutter/material.dart';
class CustomRadio extends StatefulWidget {
#override
createState() {
return new CustomRadioState();
}
}
class CustomRadioState extends State<CustomRadio> {
List<RadioModel> sampleData = new List<RadioModel>();
#override
void initState() {
// TODO: implement initState
super.initState();
sampleData.add(new RadioModel(true, 'A',0xffe6194B));
sampleData.add(new RadioModel(false, 'B',0xfff58231));
sampleData.add(new RadioModel(false, 'C',0xffffe119));
sampleData.add(new RadioModel(false, 'D',0xffbfef45));
}
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text("ListItem"),
),
body: new ListView.builder(
itemCount: sampleData.length,
itemBuilder: (BuildContext context, int index) {
return new InkWell(
splashColor: Colors.blueAccent,
onTap: () {
setState(() {
sampleData.forEach((element) => element.isSelected = false);
sampleData[index].isSelected = true;
});
},
child: new RadioItem(sampleData[index]),
);
},
),
);
}
}
class RadioItem extends StatelessWidget {
final RadioModel _item;
RadioItem(this._item);
#override
Widget build(BuildContext context) {
return new Container(
margin: new EdgeInsets.all(15.0),
child: new Row(
mainAxisSize: MainAxisSize.max,
children: <Widget>[
new Container(
height: 25.0,
width: 25.0,
alignment: Alignment.center,
child:Container(
height: 15.0,
width: 15.0,
decoration: new BoxDecoration(
color:Color(_item.colorCode),
borderRadius: const BorderRadius.all(const Radius.circular(15)),
)
),
decoration: new BoxDecoration(
color: Colors.transparent,
border: new Border.all(
width: 3.0,
color: _item.isSelected
? Color(_item.colorCode)
: Colors.transparent),
borderRadius: const BorderRadius.all(const Radius.circular(25)),
),
),
new Container(
margin: new EdgeInsets.only(left: 10.0)
)
],
),
);
}
}
class RadioModel {
bool isSelected;
final String buttonText;
final int colorCode;
RadioModel(this.isSelected, this.buttonText,this.colorCode);
}
void main() {
runApp(new MaterialApp(
home: new CustomRadio(),
));
}
Click here to check out put-> Here
Related
Need to tickmark this widget depending on drop down value pending
confirmed, dispatched, recieved.
if passed pending it display pending with tick and if its confirmed on
dropdown it shows confimed with two ticks and dispatched with three ticks
and so on. Tried creating drop down which selects the all four values dont understand how
to implement tickmarks based on the text value and show that widget that I made.
Please help. Thanks.
class OrderListScreen extends StatefulWidget {
const OrderListScreen({Key? key}) : super(key: key);
#override
State<OrderListScreen> createState() => _OrderListScreenState();
}
class _OrderListScreenState extends State<OrderListScreen> {
#override
Widget build(BuildContext context) {
return Material(
child: Container(
child: Column(
children: <Widget>[
Text(
" Please select the order status from the dropdown Below:",
style: TextStyle(backgroundColor: Colors.orange)),
Container(
child: Material(
child: DropdownButton<String>(
items: <String>[
'Pending',
'Confirmed',
'Dispatched',
'Recieved'
].map((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
}).toList(),
onChanged: (_) {},
),
)),
],
),
),
);
}
}
OrderStatus Widget (that has all dropdown values):
OrderStatusBar(title: widget.order.orderStatus, status: true),
class OrderStatusBar extends StatefulWidget {
const OrderStatusBar({Key? key, required this.title, required this.status})
: super(key: key);
final String title;
final bool status;
#override
State<OrderStatusBar> createState() => _OrderStatusBarState();
}
class _OrderStatusBarState extends State<OrderStatusBar> {
#override
Widget build(BuildContext context) {
return Directionality(
textDirection: TextDirection.rtl,
child: Row(
children: [
widget.status ? dottedCircleWithCheckMark() : dottedCircle(),
const SizedBox(width: 30),
Text(
widget.title.tr,
style: TextStyle(
fontSize: 20,
fontWeight: widget.status ? FontWeight.bold : null,
),
),
],
),
);
}
}
const size = 25.0;
const strokeWidth = 1.0;
const checkedColor = Color.fromRGBO(232, 113, 65, 1);
Widget dottedLine() {
return Directionality(
textDirection: TextDirection.rtl,
child: Align(
alignment: Alignment.topRight,
child: Container(
margin: const EdgeInsets.fromLTRB(0, 0, size / 2, 0),
child: const Padding(
padding: EdgeInsets.only(left: 27 / 2),
child: SizedBox(
height: size,
child: DottedLine(
dashColor: Colors.black,
direction: Axis.vertical,
lineLength: size,
lineThickness: strokeWidth,
dashLength: 5,
dashGapLength: 5,
),
),
),
),
),
);
}
dottedCircle() {
return DottedBorder(
borderType: BorderType.Circle,
dashPattern: const [5, 5],
child: Container(
height: size,
width: size,
decoration: const BoxDecoration(shape: BoxShape.circle),
));
}
dottedCircleWithCheckMark() {
return Container(
height: size + strokeWidth * 2,
width: size + strokeWidth * 2,
decoration: const BoxDecoration(
shape: BoxShape.circle,
color: checkedColor,
),
child: const Icon(
Icons.check,
color: Colors.white,
size: size / 4 * 3,
),
);
}
Create a callback on OrderListScreen to get selected item.
class OrderListScreen extends StatefulWidget {
final Function(String? selectedValue) callback;
const OrderListScreen({Key? key, required this.callback}) : super(key: key);
#override
State<OrderListScreen> createState() => _OrderListScreenState();
}
And get value from from onCHanged
onChanged: (v) {
widget.callback(v);
setState(() {
selectedValue = v;
});
},
Now on parent widget.
class _AppXState extends State<AppX> {
final items = <String>['Pending', 'Confirmed', 'Dispatched', 'Recieved'];
int selectedItemIndex = 0;
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: [
OrderListScreen(
callback: (selectedValue) {
if (selectedValue != null && items.contains(selectedValue)) {
selectedItemIndex = items.indexOf(selectedValue);
setState(() {});
}
},
),
for (int i = 0; i < items.length; i++)
OrderStatusBar(title: items[i], status: i <= selectedItemIndex),
],
),
);
}
}
Test snippet
class AppX extends StatefulWidget {
AppX({Key? key}) : super(key: key);
#override
State<AppX> createState() => _AppXState();
}
class _AppXState extends State<AppX> {
final items = <String>['Pending', 'Confirmed', 'Dispatched', 'Recieved'];
int selectedItemIndex = 0;
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: [
OrderListScreen(
callback: (selectedValue) {
if (selectedValue != null && items.contains(selectedValue)) {
selectedItemIndex = items.indexOf(selectedValue);
setState(() {});
}
},
),
for (int i = 0; i < items.length; i++)
OrderStatusBar(title: items[i], status: i <= selectedItemIndex),
],
),
);
}
}
class OrderListScreen extends StatefulWidget {
final Function(String? selectedValue) callback;
const OrderListScreen({Key? key, required this.callback}) : super(key: key);
#override
State<OrderListScreen> createState() => _OrderListScreenState();
}
class _OrderListScreenState extends State<OrderListScreen> {
String? selectedValue;
#override
Widget build(BuildContext context) {
return Material(
child: Container(
child: Column(
children: <Widget>[
Text(" Please select the order status from the dropdown Below:",
style: TextStyle(backgroundColor: Colors.orange)),
Container(
child: Material(
child: DropdownButton<String>(
value: selectedValue,
items: <String>[
'Pending',
'Confirmed',
'Dispatched',
'Recieved'
].map((String value) {
return DropdownMenuItem<String>(
value: value,
child: Row(
children: [
Text(value),
],
),
);
}).toList(),
onChanged: (v) {
widget.callback(v);
setState(() {
selectedValue = v;
});
},
),
)),
],
),
),
);
}
}
Hi im new to Flutter and coding and tried do build my first to do app. I've created a textformfield to add new todos with a button in a container above. By pressing a button, a new todo will appear on the todo Container. I managed to dynamically give a column new todos with a CheckboxListtTitle. However, when adding a new todo, the todo with a checkbox appears but can't be checked. What did I do wrong here?
to_do_section.dart
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/src/foundation/key.dart';
import 'package:flutter/src/widgets/framework.dart';
import 'package:flutter_application_1/presentation/widgets/landing_page.dart';
import 'package:flutter_application_1/responsive_layout.dart';
import 'package:flutter/scheduler.dart' show timeDilation;
var toDoList = <String>[userInput];
class ToDos extends StatefulWidget {
final List<String> list;
const ToDos({
Key? key,
required this.list,
}) : super(key: key);
#override
State<ToDos> createState() => _ToDosState();
}
class _ToDosState extends State<ToDos> {
#override
Widget build(BuildContext context) {
SizeConfig().init(context);
bool checked= false;
bool? value_1;
var isSelected = [false, false];
return StatefulBuilder(
builder: (BuildContext context, StateSetter setState) {
return Padding(
padding: EdgeInsets.only(
top: SizeConfig.blockSizeHorizontal * 10,
left: SizeConfig.blockSizeVertical * 2.5,
right: SizeConfig.blockSizeVertical * 2.5,
bottom: SizeConfig.screenHeight / 8),
child: SizedBox(
width: SizeConfig.blockSizeHorizontal * 100,
height: SizeConfig.blockSizeVertical * 40,
child: Container(
decoration: BoxDecoration(
color: Colors.grey[400],
borderRadius: BorderRadius.circular(30),
border: Border.all(
color: Colors.black45, style: BorderStyle.solid, width: 4)),
child: Padding(
padding: EdgeInsets.all(8.0),
child: ToDoColumn(setState, checked),
),
),
),
);
});
}
Column ToDoColumn(
StateSetter setState, bool checked) {
return Column(children: [
for (final value in widget.list)
CheckboxListTile(
value: checked,
onChanged: (_value_1) {
setState(() {
checked = _value_1!;
});
},
title: Text('${value}'),
activeColor: Colors.green,
checkColor: Colors.black,
)
]);
}
}
landing_page.dart
import 'package:flutter/material.dart';
import 'package:flutter_application_1/presentation/widgets/to_do_section.dart';
final _textController = TextEditingController();
String userInput = "";
class LandingPage extends StatefulWidget {
const LandingPage({Key? key}) : super(key: key);
#override
State<LandingPage> createState() => _LandingPageState();
}
class _LandingPageState extends State<LandingPage> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Center(child: Text("To-Do-App")),
backgroundColor: Colors.redAccent,
),
body: SingleChildScrollView(
child: Column(
children: [ToDos(list: toDoList), ToDoAdd()],
),
),
);
}
Column ToDoAdd() {
return Column(
children: [
Padding(
padding: EdgeInsets.only(top: 8.0, left: 20, right: 20, bottom: 20),
child: TextField(
onChanged: (value) => setState(() => userInput = value) ,
textAlign: TextAlign.center,
decoration: InputDecoration(
border: OutlineInputBorder(),
hintText: "Add a new ToDo",
) ,
),
),
MaterialButton(
color: Colors.redAccent,
onPressed: () {
setState(() {
toDoList.add(userInput);
});
},
child: Text("Admit", style: TextStyle(color: Colors.white),),
),
Text(userInput)
],
);
}
}
Every variable declared inside the build method will be redeclared on each build. You could move its declaration outside and assign a value in the initState method.
Also, the use of the StatefulBuilder is not justified in your code. You could simply use the StatefulWidget as it is.
I will take the model class from #yeasin-sheikh
class Task {
final String text;
final bool isChecked;
Task({
required this.text,
this.isChecked = false,
});
Task copyWith({
String? text,
bool? isChecked,
}) {
return Task(
text: text ?? this.text,
isChecked: isChecked ?? this.isChecked,
);
}
}
This example is a reduced one that accomplish what you are trying to do. Please notice that I'm not reproducing your design. The function addRandomToDo() could be replaced with your code for adding Tasks to the app.
class ToDoPage extends StatefulWidget {
const ToDoPage({Key? key}) : super(key: key);
#override
State<ToDoPage> createState() => _ToDoPageState();
}
class _ToDoPageState extends State<ToDoPage> {
final tasks = <Task>[];
void addRandomToDo() {
setState(() {
tasks.add(Task(text: 'Random ToDo ${tasks.length + 1}'));
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('ToDo App'),
),
body: ListView.builder(
itemCount: tasks.length,
itemBuilder: (context, index) {
return CheckboxListTile(
key: ObjectKey(tasks[index]),
title: Text(tasks[index].text),
value: tasks[index].isChecked,
onChanged: (isChecked) {
setState(() {
tasks[index] = tasks[index].copyWith(isChecked: isChecked);
});
},
);
}),
floatingActionButton: FloatingActionButton(
onPressed: addRandomToDo,
tooltip: 'Add ToDo',
child: const Icon(Icons.add),
),
);
}
}
Tips:
The use of Column for this example is not recommended. The best is to use a ListView.builder() and the reason is that when you use a Column and you have more element than the screen can render the app will also render the ones that are offscreen and will result in a lack of performance but the use of ListView will be best because it only renders a few elements offscreen (maybe two or three) to avoid a memory overload and it will load and render the elements as you scroll.
Is a best practice to not use functions to return a piece of the screen but to split it into widgets. (Instead of having a ToDoColumn function just change it for a ToDoColumnWidget). The explanation for this is that will incurre in a lack of performance because all functions will be re-rendered on every widget build.
This piece of code :
EdgeInsets.only(top: 8.0, left: 20, right: 20, bottom: 20)
Could be changed for this one:
EdgeInsets.fromLTRB(20, 8, 20, 20)
You have defined isSelected inside the build method. The build method is invoked each time you set the state. Please move it to initState.
bool checked= false;
Define bool checked = false outside the build method or in initstate
Putting variables inside build method will reset the bool and always be false. Also on your snippet you are just holding text without check state. On evey-build it will also reset on this point.
You can remove StatefulBuilder while this is already a StatefulWidget. Try using state-management like riverpod or bloc instead of using global variables in project cases.
As for the solution, I am creating a model class.
class Task {
final String text;
final bool isChecked;
Task({
required this.text,
this.isChecked = false,
});
Task copyWith({
String? text,
bool? isChecked,
}) {
return Task(
text: text ?? this.text,
isChecked: isChecked ?? this.isChecked,
);
}
}
Now the list will be var toDoList = [Task(text: "")];
And your ToDos
class ToDos extends StatefulWidget {
final List<Task> list;
const ToDos({
Key? key,
required this.list,
}) : super(key: key);
#override
State<ToDos> createState() => _ToDosState();
}
class _ToDosState extends State<ToDos> {
#override
Widget build(BuildContext context) {
//todo: remove [StatefulBuilder]
return StatefulBuilder(builder: (BuildContext context, setStateSB) {
return Container(
child: Padding(
padding: EdgeInsets.all(8.0),
child: ToDoColumn(setStateSB),
),
);
});
}
//current
Column ToDoColumn(
StateSetter setStateSB,
) {
return Column(children: [
for (int i = 0; i < widget.list.length; i++)
CheckboxListTile(
value: widget.list[i].isChecked,
onChanged: (_value_1) {
widget.list[i] = widget.list[i].copyWith(isChecked: _value_1);
setState(() {});
setStateSB(() {});
},
title: Text('${widget.list[i].isChecked}'),
activeColor: Colors.green,
checkColor: Colors.black,
)
]);
}
}
More about StatefulWidget and cookbook on flutter
I want to pass data onclick of the listview items from one screen to another. All screen has bottom navigation bar with end drawer. Tried to pass data from second screen to details screen but was unsuccessful as there is no Navigator used. Anyone can help me with this? Following is the implemented code
bottom_nav_bar.dart
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'Utility.dart';
import 'main.dart';
class CustomAnimatedBottomBar extends StatelessWidget {
CustomAnimatedBottomBar({
Key? key,
this.selectedIndex = ScreenType.home,
this.showElevation = true,
this.iconSize = 24,
this.backgroundColor,
this.itemCornerRadius = 40,
this.animationDuration = const Duration(milliseconds: 270),
this.mainAxisAlignment = MainAxisAlignment.spaceBetween,
required this.items,
required this.onItemSelected,
this.curve = Curves.linear,
}) : assert(items.length >= 2 && items.length <= 5),
super(key: key);
final ScreenType selectedIndex;
final double iconSize;
final Color? backgroundColor;
final bool showElevation;
final Duration animationDuration;
final List<BottomNavyBarItem> items;
final ValueChanged<ScreenType> onItemSelected;
final MainAxisAlignment mainAxisAlignment;
final double itemCornerRadius;
final Curve curve;
#override
Widget build(BuildContext context) {
final bgColor = backgroundColor ?? Theme.of(context).bottomAppBarColor;
return Container(
decoration: BoxDecoration(
color: bgColor,
boxShadow: [
if (showElevation)
const BoxShadow(
color: Colors.black12,
blurRadius: 2,
),
],
),
child: SafeArea(
child: Container(
width: double.infinity,
height: kToolbarHeight,
padding: const EdgeInsets.symmetric(vertical: 6, horizontal: 8),
child: Row(
mainAxisAlignment: mainAxisAlignment,
children: items.map((item) {
var index = item;
return GestureDetector(
onTap: () => onItemSelected(index.screenType),
child: _ItemWidget(
item: item,
iconSize: iconSize,
isSelected: index.screenType == selectedIndex,
backgroundColor: bgColor,
itemCornerRadius: itemCornerRadius,
animationDuration: animationDuration,
curve: curve,
),
);
}).toList(),
),
),
),
);
}
}
class _ItemWidget extends StatelessWidget {
final double iconSize;
final bool isSelected;
final BottomNavyBarItem item;
final Color backgroundColor;
final double itemCornerRadius;
final Duration animationDuration;
final Curve curve;
const _ItemWidget({
Key? key,
required this.item,
required this.isSelected,
required this.backgroundColor,
required this.animationDuration,
required this.itemCornerRadius,
required this.iconSize,
this.curve = Curves.linear,
}) : super(key: key);
#override
Widget build(BuildContext context) {
return Semantics(
container: true,
selected: isSelected,
child: AnimatedContainer(
width: isSelected ? 130 : 50,
height: double.maxFinite,
duration: animationDuration,
curve: curve,
decoration: BoxDecoration(
color:
isSelected ? item.activeColor.withOpacity(0.2) : backgroundColor,
borderRadius: BorderRadius.circular(itemCornerRadius),
),
child: Container(
width: isSelected ? 130 : 50,
padding: EdgeInsets.symmetric(horizontal: 10),
child: Row(
// mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
IconTheme(
data: IconThemeData(
size: iconSize,
color: isSelected
? item.activeColor.withOpacity(1)
: item.inactiveColor == null
? item.activeColor
: item.inactiveColor,
),
child: item.icon,
),
if (isSelected)
Expanded(
child: Container(
padding: EdgeInsets.symmetric(horizontal: 4),
child: DefaultTextStyle.merge(
style: TextStyle(
color: item.activeColor,
fontWeight: FontWeight.bold,
),
maxLines: 1,
textAlign: item.textAlign,
child: item.title,
),
),
),
],
),
),
),
);
}
}
class BottomNavyBarItem {
BottomNavyBarItem({
required this.screenType,
required this.icon,
required this.title,
this.activeColor = Colors.blue,
this.textAlign,
this.inactiveColor,
});
final ScreenType screenType;
final Widget icon;
final Widget title;
final Color activeColor;
final Color? inactiveColor;
final TextAlign? textAlign;
}
main.dart
import 'package:flutter/material.dart';
import 'package:flutter_app/FifthScreen.dart';
import 'package:flutter_app/details_screen.dart';
import 'package:flutter_app/profile_screen.dart';
import 'package:flutter_app/secondPage.dart';
import 'ThirdPage.dart';
import 'Utility.dart';
import 'bottom_nav_bar.dart';
import 'firstpage.dart';
import 'fourthPage.dart';
import 'home_screen.dart';
import 'message_screen.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
// This widget is the root of your application.
#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 ScreenType {
firstScreen,
secondScreen,
thirdScreen,
forthScreen,
fifthScreen,
detailsScreen,
home,
messages,
profile
}
class _MyHomePageState extends State<MyHomePage> {
ScreenType _screenType = ScreenType.home;
final _inactiveColor = Colors.grey;
GlobalKey<ScaffoldState> _scaffoldKey = new GlobalKey<ScaffoldState>();
#override
Widget build(BuildContext context) {
return Scaffold(
key: _scaffoldKey,
appBar: AppBar(
title: getTitle(_screenType),
),
endDrawer: Drawer(
child: ListView(
padding: EdgeInsets.zero,
children: <Widget>[
DrawerHeader(
child: Text('Drawer Header'),
decoration: BoxDecoration(
color: Colors.blue,
),
),
ListTile(
title: Text('First Screen'),
onTap: (){onTabTapped(ScreenType.firstScreen);
Navigator.of(context).pop();
},
),
ListTile(
title: Text('Second Screen'),
onTap:(){onTabTapped(ScreenType.secondScreen);
Navigator.of(context).pop();
},
),
ListTile(
title: Text('Third Screen'),
onTap: (){onTabTapped(ScreenType.thirdScreen);
Navigator.of(context).pop();
},
),
],
),
),
body: _body(_screenType),
bottomNavigationBar: _buildBottomBar(),
);
}
Widget _body(ScreenType screenType) {
switch (screenType) {
case ScreenType.firstScreen:
return FirstScreen(
navigateScreen: (screenType) => onTabTapped(screenType),
);
case ScreenType.secondScreen:
return SecondScreen(
onClickList: (model){
setState(() {
_screenType = ScreenType.detailsScreen;
});
},
);
case ScreenType.thirdScreen:
return const ThirdScreen();
case ScreenType.forthScreen:
return const ForthScreen();
case ScreenType.home:
return const HomeScreen();
case ScreenType.messages:
return const MessagesScreen();
case ScreenType.profile:
return const ProfileScreen();
case ScreenType.fifthScreen:
return const FifthScreen();
case ScreenType.detailsScreen:
return DetailsScreen();
}
}
Widget _buildBottomBar() {
return CustomAnimatedBottomBar(
backgroundColor: Colors.black,
selectedIndex: _screenType,
showElevation: true,
itemCornerRadius: 24,
curve: Curves.easeIn,
onItemSelected: onTabTapped,
items: <BottomNavyBarItem>[
BottomNavyBarItem(
screenType: ScreenType.home,
icon: Icon(Icons.apps),
title: Text('Home'),
activeColor: Colors.green,
inactiveColor: _inactiveColor,
textAlign: TextAlign.center,
),
BottomNavyBarItem(
screenType: ScreenType.messages,
icon: Icon(Icons.message),
title: Text('Messages'),
activeColor: Colors.purpleAccent,
inactiveColor: _inactiveColor,
textAlign: TextAlign.center,
),
BottomNavyBarItem(
screenType: ScreenType.profile,
icon: Icon(Icons.account_circle_rounded),
title: Text('Profile'),
activeColor: Colors.pink,
inactiveColor: _inactiveColor,
textAlign: TextAlign.center,
),
],
);
}
void onTabTapped(ScreenType screenType) {
if ((_scaffoldKey.currentState ?? ScaffoldState()).isEndDrawerOpen) {
(_scaffoldKey.currentState ?? ScaffoldState()).openEndDrawer();
}
setState(() {
_screenType = screenType;
});
}
Widget getTitle(ScreenType screenType) {
switch (screenType) {
case ScreenType.firstScreen:
return Text("First Screen");
case ScreenType.secondScreen:
return Text("Second Screen");
case ScreenType.thirdScreen:
return Text("Third Screen");
case ScreenType.forthScreen:
return Row(
children: [
IconButton(onPressed: (){
onTabTapped(ScreenType.firstScreen);
}, icon: Icon(Icons.arrow_back_ios)),
Text("Fourth Screen"),
],
);
case ScreenType.detailsScreen:
return Text("Details Screen");
case ScreenType.home:
return Text("Home");
case ScreenType.messages:
return Text("Message");
case ScreenType.profile:
return Text("Profile");
case ScreenType.fifthScreen:
// TODO: Handle this case.
return Text("Fifth Sceen");
break;
}
}
}
second_page.dart
import 'package:flutter/material.dart';
typedef OnClickList(Model);
class SecondScreen extends StatefulWidget {
// final VoidCallback voidCallback;
final OnClickList onClickList;
const SecondScreen({Key? key, required this.onClickList}) : super(key: key);
#override
_SecondScreenState createState() => _SecondScreenState();
}
class _SecondScreenState extends State<SecondScreen> {
#override
Widget build(BuildContext context) {
List<Model> items = [
Model(text: "Text 1 to next screen"),
Model(text: "Text 2 to next screen"),
Model(text: "Text 3 to next screen"),
];
return Container(
child: Center(
child: Column(
children: [
Text("Second Screen"),
ListView.builder(
itemCount: items.length,
shrinkWrap: true,scrollDirection: Axis.vertical,
itemBuilder: (context, index){
Model model = items[index];
return GestureDetector(
onTap: (){
widget.onClickList(model.text);
},
child: Card(
child: Text("${items[index].text}"),
),
);
})
],
),
),
);
}
}
class Model{
String text;
Model({required this.text});
}
details_page.dart
import 'package:flutter/material.dart';
import 'package:flutter_app/secondPage.dart';
class DetailsScreen extends StatefulWidget {
final Model? model;
const DetailsScreen({Key? key, this.model}):super(key:key);
#override
_DetailsScreenState createState() => _DetailsScreenState();
}
class _DetailsScreenState extends State<DetailsScreen> {
#override
Widget build(BuildContext context) {
return Container(
child: Center(
child: Text(widget.model!.text.toString()),
),
);
}
}
first_page.dart
import 'package:flutter/material.dart';
import 'package:flutter_app/main.dart';
typedef NavigateScreen(ScreenType);
class FirstScreen extends StatefulWidget {
const FirstScreen({
Key? key,
required this.navigateScreen,
}) : super(key: key);
final NavigateScreen navigateScreen;
#override
_FirstScreenState createState() => _FirstScreenState();
}
class _FirstScreenState extends State<FirstScreen> {
#override
Widget build(BuildContext context) {
return Container(
child: Center(
child: Flex(
direction: Axis.vertical,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text("First Screen"),
ElevatedButton(
onPressed: () => widget.navigateScreen(ScreenType.forthScreen),
child: Text("Go To Forth Screen"),
),
ElevatedButton(
onPressed: () => widget.navigateScreen(ScreenType.fifthScreen),
child: Text("Go To Fifth Screen"),
),
],
),
),
);
}
}
To pass data from second screen to details screen:
Add a variable in homepage state which can take content clicked from second screen.
class _MyHomePageState extends State<MyHomePage> {
ScreenType _screenType = ScreenType.home;
final _inactiveColor = Colors.grey;
Model? fromSecond;
…….
}
Change onClickList implementation and switch case.
Widget _body(ScreenType screenType) {
switch (screenType) {
case ScreenType.firstScreen:
return FirstScreen(
navigateScreen: (screenType) => onTabTapped(screenType),
);
case ScreenType.secondScreen:
return SecondScreen(
onClickList: (model) {
fromSecond = model;
setState(() {
_screenType = ScreenType.detailsScreen;
});
},
);
……..
…….
case ScreenType.detailsScreen:
{
if(fromSecond!=null) {
return DetailsScreen(model: fromSecond);
} else {
return DetailsScreen();
}
}
………..
……….
}
On SecondScreen make gesture detector return model to onClickList callback.
…………
…………
return GestureDetector(
onTap: (){
widget.onClickList(model);
},
child: Card(
child: Text("${items[index].text}"),
),
);
…….
…….
I have column of InkWells. When a button is pressed, it changes color to red from white. The problem is, when another button is pressed, the previous button doesn't change back to white.
How can I solve this?
class NavigationButton extends StatefulWidget {
const NavigationButton({
Key key,
this.title,
}) : super(key: key);
final String title;
#override
_NavigationButtonState createState() => _NavigationButtonState();
}
class _NavigationButtonState extends State<NavigationButton> {
Color _color = Colors.white;
#override
Widget build(BuildContext context) {
return Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
InkWell(
highlightColor: Colors.transparent,
splashColor: Colors.transparent,
child: Container(
child: Text(
widget.title,
style: TextStyle(color: Colors.white, fontSize: 25)
)
),
onTap: () {
setState(() {
_color = Colors.red;
});
}
),
SizedBox(width: 10),
Container(
width: 10,
height: 10,
decoration: BoxDecoration(color: _color, shape: BoxShape.circle)
)
]
);
}
}
The menu wuth navigation buttons:
child: Container(
height: 205,
width: 210,
padding: EdgeInsets.only(right: 10),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
for (int index = 0; index < _sectionsName.length; index++)
NavigationButton(title: _sectionsName[index], index: index)
]
)
)
Thanks in advance...
can be solved by taking a boolean variable isSeleted;
For the current situation you will have to take bool in the widget where you have defined the NavigationButton List and wrap navigationButton with gesturedetector.
import 'package:flutter/material.dart';
class NavigationButton extends StatefulWidget {
final buttonColor;
final textColor;
final String title;
const NavigationButton({
Key key,
this.title,
this.buttonColor,
this.textColor,
}) : super(key: key);
#override
_NavigationButtonState createState() => _NavigationButtonState();
}
class _NavigationButtonState extends State<NavigationButton> {
#override
Widget build(BuildContext context) {
return Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
SizedBox(width: 10),
Container(
decoration: BoxDecoration(
color: widget.buttonColor,
),
child: Container(
child: Text(
widget.title,
style: TextStyle(
color: widget.textColor,
fontSize: 25,
),
),
),
)
],
);
}
}
class MainScreen extends StatefulWidget {
const MainScreen({Key key}) : super(key: key);
#override
_MainScreenState createState() => _MainScreenState();
}
class _MainScreenState extends State<MainScreen> {
// List isSelected = [];
Map _sectionsName = {"one ": false, "two": false};
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
height: 205,
width: 210,
padding: EdgeInsets.only(right: 10),
child: ListView(
children: _sectionsName.keys
.map<Widget>((item) => GestureDetector(
onTap: () {
_sectionsName.forEach((key, value) {
if (key == item) {
setState(() {
_sectionsName[key] = true;
});
}else{
_sectionsName[key] = false;
}
});
},
child: NavigationButton(
title: item,
buttonColor: _sectionsName[item] == true
? Colors.red
: Colors.white,
textColor: _sectionsName[item] == true
? Colors.white
: Colors.black,
),
))
.toList(),
),
),
);
}
}
I'd like to add a Dropdown list containing "names" in my application instead of a TextField as I have in the code below.
How can I do this using BloC?
class PersonPage extends StatefulWidget {
PersonPage(this.person);
final Person person;
#override
_PersonPageState createState() => _PersonPageState();
}
class Names{
const Item(this.name);
final String name;
}
class _PersonPageState extends State<PersonPage> {
Item selectedUser;
List<Item> names = <Names>[
const Item('Thomas'),
const Item('John'),
const Item('Mary'),
const Item('Lukas'),
];
TextEditingController _nameController;
final _bloc = PersonBloc();
#override
void initState() {
_bloc.setPerson(widget.person);
_nameController = TextEditingController(text: widget.person.name);
super.initState();
}
#override
void dispose() {
_nameController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("MyApp"),
),
body: Container(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: ListView(
children: <Widget>[
Container(
child: TextField(
decoration: InputDecoration(labelText: "Name"),
controller: _nameController,
onChanged: _bloc.setName,
),
),
Container(
height: 20,
),
RaisedButton(
child: Text("Save"),
onPressed: () {
if (_bloc.insertOrUpdate()) {
Navigator.pop(context);
}
},
)
],
),
),
),
);
}
Thanks.
I'll give to you an example and you try to apply in your code
class _PersonPageState extends State<PersonPage> {
final _bloc = PersonBloc();
#override
void initState() {
_bloc.setPerson(widget.person);
_nameController = TextEditingController(text: widget.person.name);
super.initState();
}
#override
void dispose() {
_nameController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("MyApp"),
),
body: Container(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: ListView(
children: <Widget>[
StreamBuilder(
stream: bloc.outName,
builder: (context, snapshot) {
if (!snapshot.hasData)
return Container();
return DropdownButton(
hint: Text("Names"),
value: snapshot.data,
items: bloc.names.map((item) {
return DropdownMenuItem(
value: item,
child: Row(children: <Widget>[Text(item),]),
);
}).toList(),
onChanged: bloc.inName,
);
},
),
Container(
height: 20,
),
RaisedButton(
child: Text("Save"),
onPressed: () {
if (_bloc.insertOrUpdate()) {
Navigator.pop(context);
}
},
)
],
),
),
),
);
}
Change your names list to your BLoC, and create the name BehaviorSubject
PersonBloc{
List<String> names = ['Thomas', 'John', 'Mary', 'Lukas'];
final _name = BehaviorSubject<String>.seeded("");
Stream<String> get outName => _name.stream;
Function(String) get inName => _name.sink.add;
//TODO - Don't forget to dispose this _name
}
Now your _name has the selected name, and to get the name, you just need to do
Person.name = _name.stream.value;
This example can be improved, it's just a draft
this is null-safty sample with model:
import 'package:flutter/material.dart';
import 'package:rxdart/rxdart.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 CountryPage(),
);
}
}
class CountryPage extends StatefulWidget {
const CountryPage({Key? key}) : super(key: key);
#override
State<CountryPage> createState() => _CountryPageState();
}
class _CountryPageState extends State<CountryPage> {
final _bloc = CountryBloc();
#override
void initState() {
super.initState();
}
#override
void dispose() {
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("MyApp"),
),
body: Padding(
padding: const EdgeInsets.all(8.0),
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8.0),
border: Border.all(width: 1.2, color: Colors.grey)),
child: CustomDropDown(bloc: _bloc),
),
),
);
}
}
class CustomDropDown extends StatelessWidget {
const CustomDropDown({
Key? key,
required CountryBloc bloc,
}) : _bloc = bloc,
super(key: key);
final CountryBloc _bloc;
#override
Widget build(BuildContext context) {
return StreamBuilder(
stream: _bloc.streamReference,
builder: (context, snapshot) {
if (!snapshot.hasData) return Container();
return DropdownButton(
isExpanded: true,
hint: Padding(
padding: const EdgeInsets.all(8.0),
child: Text("${_bloc.getCountry.name}"),
),
underline: const SizedBox(),
icon: const Icon(Icons.keyboard_arrow_down, size: 32.0),
items: _bloc.countries.map((item) {
return DropdownMenuItem(
value: item,
child: Directionality(
textDirection: Directionality.of(context).index == 0
? TextDirection.rtl
: TextDirection.ltr,
child: Container(
margin: const EdgeInsets.only(bottom: 4.0),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8.0),
border: Border.all(width: 1.2, color: Colors.grey)),
child: Align(
alignment: AlignmentDirectional.centerStart,
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 4.0),
child: Text("${item.name}"),
)),
),
),
);
}).toList(),
onChanged: (value) {
_bloc.selectCountry(value as ObjectModel);
},
);
},
);
}
}
class CountryBloc {
List<ObjectModel> countries = [
ObjectModel(id: "0", name: 'USA'),
ObjectModel(id: "1", name: 'CHINA'),
ObjectModel(id: "2", name: 'INDIA'),
ObjectModel(id: "3", name: 'BRAZIL'),
];
final _country = BehaviorSubject<ObjectModel>.seeded(
ObjectModel(id: "-1", name: "Select Country Name.."));
Stream<ObjectModel> get streamReference => _country.stream;
Function(ObjectModel) get selectCountry => _country.sink.add;
ObjectModel get getCountry => _country.value;
void setCountry(ObjectModel country) {
_country.value = country;
}
}
class ObjectModel {
String? id;
String? name;
ObjectModel({this.id, this.name});
ObjectModel.fromJson(Map<String, dynamic> json) {
id = json['id'];
name = json['name'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data['id'] = id;
data['name'] = name;
return data;
}
}
add this dependencies to pubspec.yaml file:
flutter_bloc: ^7.1.0
rxdart: ^0.27.5