Flutter Checkbox with the radio button logic - flutter

I have been trying to get the radio button functionality using checkboxes in flutter but I could not come up with a solution for this issue. Most of the examples I come across are with the ability to select multiple checkboxes.

Please check the bellow code.
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
int index = 0;
class demo extends StatefulWidget {
#override
State<StatefulWidget> createState() => _demoState();
}
class _demoState extends State<demo> {
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
return Material(
child: SafeArea(
child: ListView.builder(
itemCount: 6,
itemBuilder: (context, i) {
return GestureDetector(
onTap: () {
setState(() {
index = i;
});
},
child: ListTile(
leading: index == i
? Icon(Icons.check_box)
: Icon(Icons.check_box_outline_blank),
title: Text(i.toString()),
),
);
},
),
),
);
}
}

Related

I want to make dropdown in expandable in all time in flutter,

I want to make this dropdown all time expandable state, don't need to un-expandable state. i want to hide or remove un-expandable state in dropdown and i want to show all time expandable to make true, its possible, is have any idea about it, please add it.
for exapmple, user click the dropdown right icon, don;t need to hide the dropdown items. how to do this
to achieve that design you can made a custom design for it. from what you explain the result should be done like this
and here is the custom code that I've created
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
final fieldText = TextEditingController();
MyApp({Key? key}) : super(key: key);
void clearText() {
fieldText.clear();
}
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(body: SafeArea(child: ListviewWithCheckBox()))
);
}
}
class ListviewWithCheckBox extends StatefulWidget {
#override
_ListviewWithCheckBoxState createState() => _ListviewWithCheckBoxState();
}
class _ListviewWithCheckBoxState extends State<ListviewWithCheckBox> {
List<String> _texts = [
"T-701 - ZONE 1/2/3",
"Slide Valves on 1st deck",
"F-301 - South side",
"Regen - 13th floor",
];
late List<bool> _isChecked;
#override
void initState() {
super.initState();
_isChecked = List<bool>.filled(_texts.length, false);
}
#override
Widget build(BuildContext context) {
return SingleChildScrollView(
child: Column(
children: [
CheckboxListTile(
controlAffinity: ListTileControlAffinity.leading,
title: Text('Oil_and_gas'),
value: false,
onChanged: (val) {
setState(
() {
// ....
},
);
},
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 30.0),
child: ListView.builder(
shrinkWrap: true,
primary: false,
itemCount: _texts.length,
itemBuilder: (context, index) {
return CheckboxListTile(
controlAffinity: ListTileControlAffinity.leading,
title: Text(_texts[index]),
value: _isChecked[index],
onChanged: (val) {
setState(
() {
// .....
},
);
},
);
},
),
),
],
),
);
}
}
that should work like a charm now

Can't add or update a list

So I'm trying to make a list that contains some widgets and then add a new widget to it when I press a button, but it doesn't seem to be working
This is the code:
class MessagesProvider extends ChangeNotifier{
List<dynamic> mesgs = [
new chatBubbleSend(),
new chatBubbleReceiver(),
new chatBubbleReceiver()
];
bool loading = true;
addMesg(){
mesgs.add(chatBubbleSend());
print(mesgs.length);
print(mesgs);
notifyListeners();
}
printMesg(){
print(mesgs.length);
print(mesgs);
}
removeMesg(){
mesgs.removeLast();
print(mesgs.length);
print(mesgs);
notifyListeners();
}
}
and this is what i get when i press the add, remove or print buttons
add,remove,print
and this is the list builder code
ChangeNotifierProvider<MessagesProvider>(
create: (context) => MessagesProvider(),
child: ChatMessages()
),
class ChatMessages extends StatelessWidget {
#override
Widget build(BuildContext context) {
final mesgs = Provider.of<MessagesProvider>(context, listen: false).mesgs;
return ListView.builder(
shrinkWrap: true,
itemCount: mesgs.length,
itemBuilder: (context,index)=> mesgs[index],
);
}
}
I have looking for a solution for over 8 hours now, and still, I couldn't fix it.
I jumped the gun with my first answer sorry.
When trying to recreate I ran into the same frustrating issue - focusing on the the provider being the problem until I realised it's actually the rendering of the updated list that's the issue.
You need to use a list builder to render the updating list in a change notifier consumer in a stateful widget
Full working example below:
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
class WidgetListProvider with ChangeNotifier {
List<Widget> widgets = [];
int listLength = 0;
void addWidget(){
Widget _widget = Text('Hello');
widgets.add(_widget);
listLength = widgets.length;
print('Added a widget');
notifyListeners();
}
void removeWidget(){
if (widgets.length > 0) {
widgets.removeLast();
listLength = widgets.length;
print('Removed a widget');
notifyListeners();
}
}
}
class HomePage extends StatefulWidget {
HomePage({Key key}) : super(key: key);
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
Widget _appBar (BuildContext context) {
return AppBar(
title: Text('My App'),
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: _appBar(context),
// You need to define widgets that update when a provider changes
// as children of a consumer of that provider
body: Consumer<WidgetListProvider>(builder: (context, widgetProvider, child){
return Container(
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
children: [
RaisedButton(
child: Text('Add widget'),
onPressed: () {
widgetProvider.addWidget();
},
),
RaisedButton(
child: Text('Remove Widget'),
onPressed: () {
widgetProvider.removeWidget();
},
),
Row(
children: [
Text('Number of Widgets: '),
Text(widgetProvider.listLength.toString()),
],
),
Container(
height: MediaQuery.of(context).size.height*0.6,
child: ListView.builder(itemCount: widgetProvider.widgets.length, itemBuilder: (BuildContext context, int index){
return widgetProvider.widgets[index];
})
)
],
),
);
}
),
);
}
}
void main() {
runApp(
ChangeNotifierProvider(
create: (context) => WidgetListProvider(),
child: MyApp(),
)
);
}
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'My App',
home: HomePage(),
);
}
}

Flutter misunderstanding - refresh and stateful widget

I'm stuck and I think I'm completely lost in Flutter's logic. I want to do the following :
class HomeScreen extends StatefulWidget {
static String id = 'home_screen';
#override
_HomeScreenState createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
actions: <Widget>[
IconButton(
icon: Icon(Icons.refresh),
onPressed: () {
** I WANT TO CALL REFRESH ON THE EVENT LIST**
},
),
],
),
body: Column(
children: <Widget>[
EventList(),
...
class EventList extends StatefulWidget {
#override
_EventListState createState() => _EventListState();
}
class _EventListState extends State<EventList> {
List<Event> eventList = [];
#override
void initState() {
super.initState();
getEventList();
}
Future<Null> getEventList() async {
// Fill eventList from a web service ...
setState(() {
});
}
#override
Widget build(BuildContext context) {
return Expanded(
child: Container(
child: (eventList == null || eventList.length == 0)
? Center(
child: CircularProgressIndicator()
: ListView.builder(
itemCount: (eventList == null) ? 0 : eventList.length,
itemBuilder: (BuildContext context, int index) {
...
This logic works to make the first fetch of data using GET on a web service.
How can I call a refresh() to get new data on the HomeScreen and ask EventList to refresh (call getEventList() again) and rebuild ?
Thank you
You can copy paste run full code below
Working demo simulate 3 seconds network delay and show random number
Step 1: call setState in IconButton's onPressed
IconButton(
icon: Icon(Icons.refresh),
onPressed: () {
setState(() {});
},
),
Step 2 : In didUpdateWidget of _EventListState use addPostFrameCallback to call getEventList()
#override
void didUpdateWidget(EventList oldWidget) {
super.didUpdateWidget(oldWidget);
WidgetsBinding.instance.addPostFrameCallback((_) {
getEventList();
});
}
working demo
Execution sequence when click refresh button
didUpdateWidget
build
getEventList
build for clear EventList
build for add EventList
full code
import 'package:flutter/material.dart';
import 'dart:math';
class Event {
String title;
Event({this.title});
}
class HomeScreen extends StatefulWidget {
static String id = 'home_screen';
#override
_HomeScreenState createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
actions: <Widget>[
IconButton(
icon: Icon(Icons.refresh),
onPressed: () {
setState(() {});
},
),
],
),
body: Column(children: <Widget>[
EventList(),
]));
}
}
class EventList extends StatefulWidget {
#override
_EventListState createState() => _EventListState();
}
class _EventListState extends State<EventList> {
List<Event> eventList = [];
#override
void initState() {
super.initState();
getEventList();
}
#override
void didUpdateWidget(EventList oldWidget) {
super.didUpdateWidget(oldWidget);
WidgetsBinding.instance.addPostFrameCallback((_) {
getEventList();
});
}
Future<Null> getEventList() async {
Random random = new Random();
setState(() {
eventList.clear();
});
await Future.delayed(Duration(seconds: 3), () {});
List<Event> newEventList = List<Event>.generate(
3, (index) => Event(title: random.nextInt(100).toString()));
setState(() {
eventList.addAll(newEventList);
});
}
#override
Widget build(BuildContext context) {
return Expanded(
child: Container(
child: (eventList == null || eventList.length == 0)
? Center(child: CircularProgressIndicator())
: ListView.builder(
itemCount: (eventList == null) ? 0 : eventList.length,
itemBuilder: (BuildContext context, int index) {
return ListTile(title: Text(eventList[index].title));
})));
}
}
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: HomeScreen(),
);
}
}
There are many ways to solve your issue.
The first one would be to call setState in your MainState class after fetching the data. This will cause all children widgets to rebuild. But to do so you will also need to save the data inside your MainState class and pass it to your EventList widget as an argument. (Note that your EventList widget does not need to be Stateful anymore! Since it should not call the setState method any longer).
The second option, if you really want your function to be inside your EventListState class, is to create a controller that you instantiate in your MainState class. But that is quite a lot of work for a task as simple as this one.
The third one is to create a Service which would be a separated class exposing a Stream on which you will push your data whenever it is needed. I know this may sound complicated so for this one here is a theoretical example:
class MyEventService {
final StreamController<List<Event>> stateStreamController = StreamController<List<Event>>.broadcast();
Stream<ActionState> get stateStream => stateStreamController.stream;
Future<void> refresh() { fetchFromServer... then stateStreamController.push(data);
}
class MainState {
build(..) => Scaffold(... Button(onPushed: myEventServiceInstance.refresh)
}
class EventList {
build(..) => StreamBuilder(stream: myEventServiceInstance.stream...)
}
I hope this helped you, do not hesitate to ask if you want a more complex sample!
in flutter you cant call a child function from a parent what I would recommend is combining them in a single stateful widget or put the scaffold in the eventList widget so the appbar and the ListView are in the same statfull Widget
class HomeScreen extends StatefulWidget {
static String id = 'home_screen';
#override
_HomeScreenState createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
List<Event> eventList = [];
#override
void initState() {
super.initState();
getEventList();
}
Future<Null> getEventList() async {
// Fill eventList from a web service ...
setState(() {
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
actions: <Widget>[
IconButton(
icon: Icon(Icons.refresh),
onPressed: () => getEventList(),
),
],
),
body: Column(
children: <Widget>[
buildEventList(),
]
),);
}
Widget buildEventList()(BuildContext context) {
return Expanded(
child: Container(
child: (eventList == null || eventList.length == 0)
? Center(
child: CircularProgressIndicator()
: ListView.builder(
itemCount: (eventList == null) ? 0 : eventList.length,
itemBuilder: (BuildContext context, int index) {
..
}),
}
}

How to manage state of two different stateful widget

The basic outline of the widget is shown Below. MyListViewBuilder1 and MyListViewBuilder2 both stateful widget are defined in separate dart file. Both of them consists of ListViewBuilder.
What I want to achieve is that when the item of MyListViewBuilder1 gets deleted then deleted item appears in MyListViewBuilder2, But the problem is this happens only when I restart the screen.
So how can I change solve this?
How can I change the state of next when state of one is changed?
Column(
children: <Widget>[
MyListViewBuilder1(),
MyListViewBuilder2()
]
)
You could use the provider package to manage state in different widgets throughout the application. In the below example, when an item is deleted in the MyListViewBuilder1, it is removed from the list and added to the list of deleted items in the ItemChangeNotifier class. The MyListViewBuilder2 has its own independent list of items, however in its build method it watches for any changes to the list of deleted items in the ItemChangeNofifier class and adds these deleted items to its own independent list of items.
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
void main() {
runApp(
MultiProvider(
providers: [
ChangeNotifierProvider<ItemChangeNotifier>(
create: (context) {
return ItemChangeNotifier();
},
),
],
child: MyApp(),
),
);
}
class Item {
int id;
String title;
Item({
this.id,
this.title,
});
}
class ItemChangeNotifier extends ChangeNotifier {
final _deletedItems = <Item>[];
List<Item> get deletedItems => List.unmodifiable(_deletedItems);
void deleteItem(Item item) {
_deletedItems.add(item);
notifyListeners();
}
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: HomePage(),
);
}
}
class HomePage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: <Widget>[
Expanded(
child: MyListViewBuilder1(),
),
Divider(
thickness: 8,
),
Expanded(
child: MyListViewBuilder2(),
),
],
),
);
}
}
class MyListViewBuilder1 extends StatefulWidget {
#override
_MyListViewBuilder1State createState() => _MyListViewBuilder1State();
}
class _MyListViewBuilder1State extends State<MyListViewBuilder1> {
List<Item> _items;
#override
Widget build(BuildContext context) {
return ListView.builder(
itemBuilder: (context, index) {
return ListTile(
title: Text(_items[index].title),
trailing: IconButton(
icon: Icon(Icons.delete),
onPressed: () {
context.read<ItemChangeNotifier>().deleteItem(_items[index]);
setState(() {
_items.removeAt(index);
});
},
),
);
},
itemCount: _items.length,
);
}
#override
void initState() {
super.initState();
_items = List.generate(
10,
(index) => Item(
id: index + 1,
title: 'Item ${index + 1}',
),
);
}
}
class MyListViewBuilder2 extends StatefulWidget {
#override
_MyListViewBuilder2State createState() => _MyListViewBuilder2State();
}
class _MyListViewBuilder2State extends State<MyListViewBuilder2> {
List<Item> _items;
#override
Widget build(BuildContext context) {
final items = [
..._items,
...context.select<ItemChangeNotifier, List<Item>>(
(itemChangeNotifier) => itemChangeNotifier.deletedItems,
),
];
return ListView.builder(
itemBuilder: (context, index) {
return ListTile(
title: Text(items[index].title),
);
},
itemCount: items.length,
);
}
#override
void initState() {
super.initState();
_items = List.generate(
10,
(index) => Item(
id: index + 101,
title: 'Item ${index + 101}',
),
);
}
}

Flutter Custom Multi Selection

Question
Hi, how can I implement a custom multi selection widget in flutter? Here is an example :
I have a list of widget created with a ListView.builder and I simply want to change color based on userTap.
How can I implement this? I'm able to change color when user tap on one option but then, when another button is tapped I can't understand how to reverse the state of the old selected option.
I was searching for a solution with flutter standard state management or maybe with bloc library.
example code
class Test extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: ListView.builder(
itemCount: 10,
itemBuilder: (context, index){
return Padding(
padding: const EdgeInsets.only(bottom: 8.0),
child: SelectableContainer(text: 'option', index: index,),
);
},
),
);
}
}
class SelectableContainer extends StatefulWidget {
final String text;
final int index;
SelectableContainer({
#required this.text,
#required this.index
});
#override
_SelectableContainerState createState() => _SelectableContainerState();
}
class _SelectableContainerState extends State<SelectableContainer> {
bool _isSelected = false;
Color unselectedColor = Colors.white;
Color selectedColor = Colors.blue;
#override
Widget build(BuildContext context) {
return GestureDetector(
onTap: (){
setState(() {
_isSelected = true;
});
},
child: AnimatedContainer(
duration: Duration(milliseconds: 400),
height: 50,
color: _isSelected ? selectedColor : unselectedColor,
child: Center(
child: Text(widget.text),
),
),
);
}
}
Thanks
First store the selection in list when the user select an item
selectionList.add(title.id);
Then in the ListView.builder change the color of the title if it's in the selectionList
Title(color: selectionList.contain(listOfTitls[index].id)? Colors.green : Colors.White);
update
this trick will do it for you
return GestureDetector(
onTap: (){
setState(() {
_isSelected =_isSelected? false:true;//this line
});
},
In your setState method, you can just do _isSelected = !_isSelected; instead of always setting it to true. That way the tile will get unselected if _isSelected is set to true.
Although the above way wil solve your problem, there is also another widget flutter has for this same scenario called ListTile. Here is a link to the documentation. You might also want to check that out. It also has an onTap callback built into it directly.
Here is a quick app I built using ListTile. Hope it solves your problem. Cheers!
import 'package:flutter/material.dart';
class DemoClass extends StatefulWidget {
_DemoClassState createState() => _DemoClassState();
}
class _DemoClassState extends State<DemoClass> {
int _selectedIndex = -1;
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: ListView.builder(
itemCount: 10,
itemBuilder: (context, index) {
return AnimatedContainer(
duration: Duration(milliseconds: 300),
height: 50,
color: _selectedIndex == index ? Colors.blue : Colors.transparent,
child: ListTile(
title: Text('This is some title'),
onTap: () => setState(() {
_selectedIndex = index;
}),
),
);
}
),
);
}
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: Center(
child: Container(
child: DemoClass(),
),
),
);
}
}
void main() {
runApp(MyApp());
}