Change color back after setState - flutter

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(),
),
),
);
}
}

Related

Show tickmark depending on drop down value in Dart

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;
});
},
),
)),
],
),
),
);
}
}

How do I populate a variable by getting a value from a stateful widget?

I have a variable that is supposed to be populated when an if statement is true.
The if statement is supposed to be true after a value is updated from a stateful widget when pressed.
The stateful widget is _BudgetCategoryCard and it has a bool that is set true when pressed. After being pressed, the value changes and the color of the card turns green as shown in this line: color: widget.hasBeenPressed ? Colors.green : Colors.white
However, after the value of hasBeenPressed has been set true, this if statement should be true but it isn't
if (budgetCategoryCards[index].getHasBeenPressed()) {
setState(() {
selectedBudget = budgetCategoryCards[index].getBudgetName();
});
}
I'm not sure if there is a better way/practice for retrieving values from a stateful widget or if parts of this code should be re-written for improvement but if anyone could recommend changes that would also be tremendously appreciated.
I tried simplifying the code and apologies for the bad code.
Does anyone know why this variable selectedBudget is not getting populated?
main
import 'package:flutter/material.dart';
import 'package:stackoverflow/home_page.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
List<String> budgets = ["gas", "food", "clothes"];
return MaterialApp(
home: CreateExpenseCardScreen(budgetsList: budgets),
);
}
}
home page
import 'package:flutter/material.dart';
class CreateExpenseCardScreen extends StatefulWidget {
const CreateExpenseCardScreen({
Key? key,
required this.budgetsList,
}) : super(key: key);
final List<String> budgetsList;
#override
State<CreateExpenseCardScreen> createState() =>
_CreateExpenseCardScreenState();
}
class _CreateExpenseCardScreenState extends State<CreateExpenseCardScreen> {
String selectedBudget = "";
#override
Widget build(BuildContext context) {
List<_BudgetCategoryCard> budgetCategoryCards = List.generate(
widget.budgetsList.length,
(index) {
return _BudgetCategoryCard(
budgetName: widget.budgetsList[index],
hasBeenPressed: false,
);
},
);
return Scaffold(
body: SingleChildScrollView(
child: Column(
children: [
SizedBox(height: 100),
...List.generate(
budgetCategoryCards.length,
(index) {
// why is if statement never true even after pressing card?
//
// as a result, selectedBudget doesnt get assigned a value
if (budgetCategoryCards[index].getHasBeenPressed()) {
setState(() {
selectedBudget = budgetCategoryCards[index].getBudgetName();
});
}
return budgetCategoryCards[index];
},
),
Padding(
padding: const EdgeInsets.all(16.0),
child: GestureDetector(
onTap: () {
if (selectedBudget.isEmpty) {
// send error
} else {
Navigator.pop(
context,
[
selectedBudget,
],
);
}
},
child: Container(
height: 50,
color: Colors.green,
child: const Center(
child: Padding(
padding: EdgeInsets.all(8.0),
child: Text(
"Create",
style: TextStyle(
fontSize: 18,
color: Colors.white,
),
),
),
),
),
),
),
],
),
),
);
}
}
class _BudgetCategoryCard extends StatefulWidget {
_BudgetCategoryCard({
Key? key,
required this.budgetName,
required this.hasBeenPressed,
}) : super(key: key);
final String budgetName;
bool hasBeenPressed;
bool getHasBeenPressed() {
return hasBeenPressed;
}
String getBudgetName() {
return budgetName;
}
#override
State<_BudgetCategoryCard> createState() => _BudgetCategoryCardState();
}
class _BudgetCategoryCardState extends State<_BudgetCategoryCard> {
#override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 8.0, horizontal: 16.0),
child: GestureDetector(
onTap: () {
setState(() {
widget.hasBeenPressed = true;
});
},
child: Container(
height: 50,
color: widget.hasBeenPressed ? Colors.green : Colors.white,
child: Center(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Text(
widget.budgetName,
style: TextStyle(
color: widget.hasBeenPressed
? Colors.white
: Colors.black.withOpacity(0.5),
),
),
),
),
),
),
);
}
}

how to perform drag operation on widget with Gesturedetector flutter

I want to drag and drop my custom widget with gesture detector. It is showing x- direction and y- direction values but not dragging to anywhere on screen.
Here is my code:
layoutpage:
SizedBox(
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height,
child: Stack(
children: _layoutProvider.tables
.map(
(SectionTable sTable) => Positioned(
top: sTable.top,
left: sTable.left,
child: LayoutWidget(
width: sTable.width,
height: sTable.height,
type: sTable.type.index,
name: sTable.name,
left: sTable.left,
top: sTable.top,
rotate: sTable.rotate,
color: sTable.order != null
? Colors.green
: Colors.grey,
seats: sTable.seats,
),
),
)
.toList()),
),
LayoutWidget:
class LayoutWidget extends StatefulWidget {
late double width;
late double height;
late double left;
late double top;
LayoutWidget({
Key? key,
required this.width,
required this.height,
required this.left,
required this.top,
}) : super(key: key);
#override
State<StatefulWidget> createState() => _LayoutWidgetState();
}
class _LayoutWidgetState extends State<LayoutWidget> {
#override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
RotationTransition(
turns: widget.type == 0
? const AlwaysStoppedAnimation(0)
: AlwaysStoppedAnimation(rotationValue / 360),
child: GestureDetector(
onPanUpdate: (details) {
widget.top = widget.top+ details.delta.dy;
widget.left = widget.left+ details.delta.dx;
setState(() {
});
},
onTap: () {
setState(() {
showMenu = !showMenu;
});
},
child: myWidget()
}
Can anyone help why i am unable to drag on screen. Thanks.
I hope you you can get Idea from this code. In this code you can drag Container anywhere in the Screen and set it. And also check this Gesture Detector Overview for Gesture Detector detail.
import 'package:flutter/cupertino.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
class GestureDetectorPage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
color: Colors.grey[100],
width: double.infinity,
height: double.infinity,
child: MainContent(),
),
);
}
}
class MainContent extends StatefulWidget {
#override
_MainContentState createState() => _MainContentState();
}
class _MainContentState extends State<MainContent> {
GlobalKey key = GlobalKey();
String dragDirection = '';
String startDXPoint = '50';
String startDYPoint = '50';
String dXPoint;
String dYPoint;
String velocity;
#override
Widget build(BuildContext context) {
return GestureDetector(
onHorizontalDragStart: _onHorizontalDragStartHandler,
onVerticalDragStart: _onVerticalDragStartHandler,
onHorizontalDragUpdate: _onDragUpdateHandler,
onVerticalDragUpdate: _onDragUpdateHandler,
onHorizontalDragEnd: _onDragEnd,
onVerticalDragEnd: _onDragEnd,
dragStartBehavior: DragStartBehavior.start, // default
behavior: HitTestBehavior.translucent,
child: Stack(
children: [
Positioned(
left: double.parse(this.startDXPoint),
top: double.parse(this.startDYPoint),
child: Container(
decoration: BoxDecoration(
color: Colors.green,
borderRadius: BorderRadius.circular(100)
),
child: Center(
child: Padding(
padding: const EdgeInsets.all(20),
child: Text('Draggable', style: TextStyle(fontSize: 14, color: Colors.white),),
),
),
)
),
],
),
);
}
void _onHorizontalDragStartHandler(DragStartDetails details) {
setState(() {
this.dragDirection = "HORIZONTAL";
this.startDXPoint = '${details.globalPosition.dx.floorToDouble()}';
this.startDYPoint = '${details.globalPosition.dy.floorToDouble()}';
});
}
/// Track starting point of a vertical gesture
void _onVerticalDragStartHandler(DragStartDetails details) {
setState(() {
this.dragDirection = "VERTICAL";
this.startDXPoint = '${details.globalPosition.dx.floorToDouble()}';
this.startDYPoint = '${details.globalPosition.dy.floorToDouble()}';
});
}
void _onDragUpdateHandler(DragUpdateDetails details) {
setState(() {
this.dragDirection = "UPDATING";
this.startDXPoint = '${details.globalPosition.dx.floorToDouble()}';
this.startDYPoint = '${details.globalPosition.dy.floorToDouble()}';
});
}
/// Track current point of a gesture
void _onHorizontalDragUpdateHandler(DragUpdateDetails details) {
setState(() {
this.dragDirection = "HORIZONTAL UPDATING";
this.dXPoint = '${details.globalPosition.dx.floorToDouble()}';
this.dYPoint = '${details.globalPosition.dy.floorToDouble()}';
this.velocity = '';
});
}
/// Track current point of a gesture
void _onVerticalDragUpdateHandler(DragUpdateDetails details) {
setState(() {
this.dragDirection = "VERTICAL UPDATING";
this.dXPoint = '${details.globalPosition.dx.floorToDouble()}';
this.dYPoint = '${details.globalPosition.dy.floorToDouble()}';
this.velocity = '';
});
}
/// What should be done at the end of the gesture ?
void _onDragEnd(DragEndDetails details) {
double result = details.velocity.pixelsPerSecond.dx.abs().floorToDouble();
setState(() {
this.velocity = '$result';
});
}
}
You can use the Draggable widget instead. Please try this
import 'package:flutter/material.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
static const String _title = 'Flutter Code Sample';
#override
Widget build(BuildContext context) {
return MaterialApp(
title: _title,
home: Scaffold(
appBar: AppBar(title: const Text(_title)),
body: const MyStatefulWidget(),
),
);
}
}
class MyStatefulWidget extends StatefulWidget {
const MyStatefulWidget({Key? key}) : super(key: key);
#override
State<MyStatefulWidget> createState() => _MyStatefulWidgetState();
}
class _MyStatefulWidgetState extends State<MyStatefulWidget> {
int acceptedData = 0;
#override
Widget build(BuildContext context) {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
Draggable<int>(
// Data is the value this Draggable stores.
data: 10,
feedback: Container(
color: Colors.deepOrange,
height: 100,
width: 100,
child: const Icon(Icons.directions_run),
),
childWhenDragging: Container(
height: 100.0,
width: 100.0,
color: Colors.pinkAccent,
child: const Center(
child: Text('Child When Dragging'),
),
),
child: Container(
height: 100.0,
width: 100.0,
color: Colors.lightGreenAccent,
child: const Center(
child: Text('Draggable'),
),
),
),
DragTarget<int>(
builder: (
BuildContext context,
List<dynamic> accepted,
List<dynamic> rejected,
) {
return Container(
height: 100.0,
width: 100.0,
color: Colors.cyan,
child: Center(
child: Text('Value is updated to: $acceptedData'),
),
);
},
onAccept: (int data) {
setState(() {
acceptedData += data;
});
},
),
],
);
}
}

How to pass listview data while using bottom navigation bar in 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}"),
),
);
…….
…….

Flutter : Custom Radio Button

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