how to use image in ToggleButtons? - flutter

I need to use ToggleButtons with two options using photos in jpg, is it possible?
I tried this code.
<const List<Widget> imgList = <Widget>[
ImageIcon(AssetImage("lib/images/10x15.jpg"), size: 80),
ImageIcon(AssetImage("lib/images/pk_sb.jpg"), size: 80),
ImageIcon(AssetImage("lib/images/pk_sm.jpg"), size: 80),
];
...
ToggleButtons(
direction: vertical ? Axis.vertical : Axis.horizontal,
onPressed: (int index) {
setState(() {
// The button that is tapped is set to true, and the others to false.
for (int i = 0; i < _selectedWeather.length; i++) {
_selectedWeather[i] = i == index;
}
});
},
borderRadius: const BorderRadius.all(Radius.circular(8)),
selectedBorderColor: Colors.blue[700],
selectedColor: Colors.white,
fillColor: Colors.blue[200],
color: Colors.blue[400],
isSelected: _selectedWeather,
children: imgList,
),
...
I need to show the buttons with this image, but only two blue rectangles appear
If it is not possible to replace the icons how could I implement this?

Related

Flutter responsive Row or Row alternative suggestions

I'm pretty new to Flutter, and I wanted to create a folder effect with tabs for my portfolio website where you click on the tabs to change what displayed in the center of the folder. Right now the buttons are dynamically generated as children of a row widget. The problem is that at smaller screen widths the text is either too small or gets cut off altogether. I even tried to figure out how to do a sort of multi-line row but gave up.
What I would ideally like is that the buttons wrap such that any buttons that would make the parent too long are placed on a separate row. However, I'm open to any solution that allows all the tabs to fit on screen without the text in the buttons being super shrunk.
My current solutions as you can see is to just scale the text down at smaller screen widths so that its all at least visible. I'm guessing that I'm either going to have redesign the way I implement the effect, or there's some relatively simple solution that I'm not aware of. I also tried replacing the Row widget that wraps the FolderButtons to a Wrap with no other modifications and that just caused the text to disappear.
Note: The way its currently implemented assumes and depends upon the folder being a perfect square.
Below are Folder, FolderButton, and ScaleSize classes respectively:
class Folder extends StatefulWidget {
const Folder({super.key});
static Column? getStaticPages(String page, BuildContext context) {
Map<String, Column> pages = {
"Welcome": Column(
children: [
Text(
'Welcome',
style: TextStyle(color: Colors.deepPurpleAccent.shade100),
)
],
),
"Web Dev's Handbook": Column(
children: [
Text(
"Web Dev's Handbook",
style: TextStyle(color: Colors.deepPurpleAccent.shade100),
),
TextButton(
onPressed: () => {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const Contents()))
},
child: const Text("Go"))
],
),
"Interactive Resume": Column(
children: [
Text(
'Interactive Resume',
style: TextStyle(color: Colors.deepPurpleAccent.shade100),
)
],
),
"Settings": Column(
children: [
Text(
'Settings',
style: TextStyle(color: Colors.deepPurpleAccent.shade100),
)
],
),
"Credits": Column(
children: [
Text(
'Credits',
style: TextStyle(color: Colors.deepPurpleAccent.shade100),
)
],
),
};
return pages[page];
}
static List<Map<String, dynamic>> staticTabs = [
{"title": "Welcome"},
{"title": "Web Dev's Handbook"},
{"title": "Interactive Resume"},
{"title": "Settings"},
{"title": "Credits"},
];
static List<FolderButton> generateTabs(int selectedTab, Function setTab) {
List<FolderButton> newTabs = [];
for (int x = 0; x < staticTabs.length; x++) {
bool selected;
if (selectedTab == x) {
selected = true;
} else {
selected = false;
}
newTabs.add(FolderButton(
title: staticTabs[x]["title"],
count: x,
selected: selected,
setTab: setTab));
}
return newTabs;
}
#override
State<Folder> createState() => _FolderState();
}
class _FolderState extends State<Folder> {
int _selectedTab = 0;
void _setTab(int count) {
setState(() {
_selectedTab = count;
});
}
#override
Widget build(BuildContext context) {
return Center(
child: Padding(
padding: const EdgeInsets.only(left: 1, right: 1, top: 20),
child: SizedBox(
height: 750,
width: 750,
child: Column(
children: [
Flexible(
flex: 1,
child: Padding(
padding: const EdgeInsets.only(left: 7.0),
child: Row(
children: Folder.generateTabs(_selectedTab, _setTab),
),
),
),
Flexible(
flex: 15,
fit: FlexFit.tight,
child: Container(
decoration: BoxDecoration(
color: Colors.deepPurpleAccent,
borderRadius: BorderRadius.circular(5)),
child: Padding(
padding: const EdgeInsets.all(5.0),
child: Container(
width: 800,
decoration:
const BoxDecoration(color: Colors.deepPurple),
child: Folder.getStaticPages(
Folder.staticTabs[_selectedTab]["title"], context)),
),
),
),
],
),
),
),
);
}
}
class FolderButton extends StatefulWidget {
const FolderButton(
{super.key,
required this.title,
required this.count,
this.selected = false,
required this.setTab});
final String title;
final int count;
final bool selected;
final Function setTab;
static final theme = <String, dynamic>{
"button": <String, dynamic>{
"picked": <bool, ButtonStyle>{
true: TextButton.styleFrom(
backgroundColor: Colors.deepPurple,
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(5), topRight: Radius.circular(5)),
side: BorderSide(
color: Colors.deepPurpleAccent,
strokeAlign: StrokeAlign.outside))),
false: TextButton.styleFrom(
backgroundColor: Colors.deepPurple,
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(5), topRight: Radius.circular(5)),
side: BorderSide(
color: Colors.deepPurple, strokeAlign: StrokeAlign.outside)),
),
}
},
// TODO Make it so I don't need to do it like this
"padding": <bool, dynamic>{
true: const EdgeInsets.only(top: 3, left: 3, right: 3),
false: const EdgeInsets.only(top: 3, left: 3, right: 3)
}
};
static Color? getTabShading(selected) {
if (selected) {
return Colors.deepPurpleAccent;
}
return Colors.deepPurple;
}
static EdgeInsetsGeometry getTabPadding(selected) {
return theme["padding"][selected];
}
#override
State<FolderButton> createState() => _FolderButtonState();
}
class _FolderButtonState extends State<FolderButton> {
void changeSelected() {}
#override
Widget build(BuildContext context) {
return Flexible(
child: Container(
height: 100,
// Button Container
decoration: BoxDecoration(
// Container Decorations
color: FolderButton.getTabShading(widget.selected),
borderRadius: const BorderRadius.only(
topLeft: Radius.circular(5),
topRight: Radius.circular(5),
)),
// Button Padding
child: Padding(
padding: FolderButton.getTabPadding(widget.selected),
// Button
child: TextButton(
onPressed: () {
widget.setTab(widget.count);
},
// Style of button itself
style: FolderButton.theme["button"]?["picked"][widget.selected],
child: Text(
textAlign: TextAlign.center,
textScaleFactor: ScaleSize.textScaleFactor(context,
maxTextScaleFactor: 1.5),
// Text of the button
widget.title,
style: TextStyle(
color: Colors.deepPurpleAccent.shade100,
fontSize: 10,
height: 1))),
),
),
);
}
}
class ScaleSize {
static double textScaleFactor(BuildContext context,
{double maxTextScaleFactor = 2}) {
final width = MediaQuery.of(context).size.width;
double val = (width / 1400) * maxTextScaleFactor;
return max(1, min(val, maxTextScaleFactor));
}
}
Any general flutter tips, tricks, conventions, and/or all-around good ideas are appreciated.
Perhaps Wrap widget is what you need. It works just like a Row until you reach the limit of the horizontal size, and then places the next widget in a new "Row" below the previous.
Try using Expanded as a child for Row/Column or whatever you need there. I'm giving you a brief description from the flutter page. I think that for text that does not have a specific width it will be good.
https://api.flutter.dev/flutter/widgets/Expanded-class.html
The answer is simply to rebuild the component with Wrap as the parent to the Buttons as it wrap overflowing children below the rest. I will also be sure to maintain a bit more forethought in terms of responsive layout design while making use of the responsive framework package.

How to change selected letter when scrolling alphabet scroll view?

I have implemented https://pub.dev/packages/alphabet_scroll_view in my project, I edited it for my own customization but when I scroll list with items scroll with letters is not changing.
https://imgur.com/a/ResAVrE here is a video to better understand what I want to achieve. That changes on selected letter is just my tap on them.
Here is other link if above is not working https://drive.google.com/file/d/1Oy6XWalXwXM-yqk7IZU0Av2_jEL3ZNk1/view?usp=sharing. I hope this will working.
I want to change selected letter dynamically when I am scrolling items.
Here is my code:
import 'package:alphabet_scroll_view/src/meta.dart';
import 'package:collection/collection.dart' show IterableExtension;
import 'package:flutter/material.dart';
enum LetterAlignment { left, right }
class AlphabetScrollView extends StatefulWidget {
AlphabetScrollView(
{Key? key,
required this.list,
this.alignment = LetterAlignment.right,
this.isAlphabetsFiltered = true,
this.overlayWidget,
required this.selectedTextStyle,
required this.unselectedTextStyle,
this.itemExtent = 40,
required this.itemBuilder})
: super(key: key);
/// List of Items should be non Empty
/// and you must map your
/// ```
/// List<T> to List<AlphaModel>
/// e.g
/// List<UserModel> _list;
/// _list.map((user)=>AlphaModel(user.name)).toList();
/// ```
/// where each item of this ```list``` will be mapped to
/// each widget returned by ItemBuilder to uniquely identify
/// that widget.
final List<AlphaModel> list;
/// ```itemExtent``` specifies the max height of the widget returned by
/// itemBuilder if not specified defaults to 40.0
final double itemExtent;
/// Alignment for the Alphabet List
/// can be aligned on either left/right side
/// of the screen
final LetterAlignment alignment;
/// defaults to ```true```
/// if specified as ```false```
/// all alphabets will be shown regardless of
/// whether the item in the [list] exists starting with
/// that alphabet.
final bool isAlphabetsFiltered;
/// Widget to show beside the selected alphabet
/// if not specified it will be hidden.
/// ```
/// overlayWidget:(value)=>
/// Container(
/// height: 50,
/// width: 50,
/// alignment: Alignment.center,
/// color: Theme.of(context).primaryColor,
/// child: Text(
/// '$value'.toUpperCase(),
/// style: TextStyle(fontSize: 20, color: Colors.white),
/// ),
/// )
/// ```
final Widget Function(String)? overlayWidget;
/// Text styling for the selected alphabet by which
/// we can customize the font color, weight, size etc.
/// ```
/// selectedTextStyle:
/// TextStyle(
/// fontWeight: FontWeight.bold,
/// color: Colors.black,
/// fontSize: 20
/// )
/// ```
final TextStyle selectedTextStyle;
/// Text styling for the unselected alphabet by which
/// we can customize the font color, weight, size etc.
/// ```
/// unselectedTextStyle:
/// TextStyle(
/// fontWeight: FontWeight.normal,
/// color: Colors.grey,
/// fontSize: 18
/// )
/// ```
final TextStyle unselectedTextStyle;
/// The itemBuilder must return a non-null widget and the third paramter id specifies
/// the string mapped to this widget from the ```[list]``` passed.
Widget Function(BuildContext, int, String) itemBuilder;
#override
_AlphabetScrollViewState createState() => _AlphabetScrollViewState();
}
class _AlphabetScrollViewState extends State<AlphabetScrollView> {
void init() {
widget.list
.sort((x, y) => x.key.toLowerCase().compareTo(y.key.toLowerCase()));
_list = widget.list;
setState(() {});
/// filter Out AlphabetList
if (widget.isAlphabetsFiltered) {
List<String> temp = [];
alphabets.forEach((letter) {
AlphaModel? firstAlphabetElement = _list.firstWhereOrNull(
(item) => item.key.toLowerCase().startsWith(letter.toLowerCase()));
if (firstAlphabetElement != null) {
temp.add(letter);
}
});
_filteredAlphabets = temp;
} else {
_filteredAlphabets = alphabets;
}
calculateFirstIndex();
setState(() {});
}
#override
void initState() {
init();
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
listController.addListener(() {
print('scrolling ${listController.position.pixels}');
if (listController.position.pixels >=
listController.position.maxScrollExtent) {
print('achieved end');
} else if (listController.position.pixels <=
listController.position.minScrollExtent) {
print('achieved start');
}
});
});
if (listController.hasClients) {
maxScroll = listController.position.maxScrollExtent;
}
super.initState();
}
ScrollController listController = ScrollController();
final _selectedIndexNotifier = ValueNotifier<int>(0);
final positionNotifer = ValueNotifier<Offset>(Offset(0, 0));
final Map<String, int> firstIndexPosition = {};
List<String> _filteredAlphabets = [];
final letterKey = GlobalKey();
List<AlphaModel> _list = [];
bool isLoading = false;
bool isFocused = false;
final key = GlobalKey();
#override
void didUpdateWidget(covariant AlphabetScrollView oldWidget) {
super.didUpdateWidget(oldWidget);
if (oldWidget.list != widget.list ||
oldWidget.isAlphabetsFiltered != widget.isAlphabetsFiltered) {
_list.clear();
firstIndexPosition.clear();
init();
}
}
int getCurrentIndex(double vPosition) {
double kAlphabetHeight = letterKey.currentContext!.size!.height;
return (vPosition ~/ kAlphabetHeight);
}
/// calculates and Maintains a map of
/// [letter:index] of the position of the first Item in list
/// starting with that letter.
/// This helps to avoid recomputing the position to scroll to
/// on each Scroll.
void calculateFirstIndex() {
_filteredAlphabets.forEach((letter) {
AlphaModel? firstElement = _list.firstWhereOrNull(
(item) => item.key.toLowerCase().startsWith(letter));
if (firstElement != null) {
int index = _list.indexOf(firstElement);
firstIndexPosition[letter] = index;
}
});
}
void scrolltoIndex(int x, Offset offset) {
int index = firstIndexPosition[_filteredAlphabets[x].toLowerCase()]!;
final scrollToPostion = widget.itemExtent * index;
if (index != null) {
listController.animateTo((scrollToPostion),
duration: const Duration(milliseconds: 300), curve: Curves.easeOut);
}
positionNotifer.value = offset;
}
void onVerticalDrag(Offset offset) {
int index = getCurrentIndex(offset.dy);
if (index < 0 || index >= _filteredAlphabets.length) return;
_selectedIndexNotifier.value = index;
setState(() {
isFocused = true;
});
scrolltoIndex(index, offset);
}
double? maxScroll;
#override
Widget build(BuildContext context) {
return Stack(
children: [
ListView.builder(
controller: listController,
scrollDirection: Axis.vertical,
itemCount: _list.length,
physics: ClampingScrollPhysics(),
itemBuilder: (_, x) {
return ConstrainedBox(
constraints: BoxConstraints(maxHeight: widget.itemExtent),
child: widget.itemBuilder(_, x, _list[x].key));
}),
Align(
alignment: widget.alignment == LetterAlignment.left
? Alignment.centerLeft
: Alignment.centerRight,
child: Container(
key: key,
padding: const EdgeInsets.symmetric(horizontal: 2),
child: SingleChildScrollView(
child: GestureDetector(
onVerticalDragStart: (z) => onVerticalDrag(z.localPosition),
onVerticalDragUpdate: (z) => onVerticalDrag(z.localPosition),
onVerticalDragEnd: (z) {
setState(() {
isFocused = false;
});
},
child: ValueListenableBuilder<int>(
valueListenable: _selectedIndexNotifier,
builder: (context, int selected, Widget? child) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: List.generate(
_filteredAlphabets.length,
(x) => GestureDetector(
key: x == selected ? letterKey : null,
onTap: () {
_selectedIndexNotifier.value = x;
scrolltoIndex(x, positionNotifer.value);
},
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.only(
topRight: widget.alignment == LetterAlignment.left ? Radius.circular(20) : Radius.circular(0),
bottomRight: widget.alignment == LetterAlignment.left ? Radius.circular(20) : Radius.circular(0),
topLeft: widget.alignment == LetterAlignment.right ? Radius.circular(20) : Radius.circular(0),
bottomLeft: widget.alignment == LetterAlignment.right ? Radius.circular(20) : Radius.circular(0)
),
color: selected == x ? Color(0xFFFA3B71) : Colors.transparent
),
padding: const EdgeInsets.symmetric(
horizontal: 12, vertical: 2),
child: Text(
_filteredAlphabets[x].toUpperCase(),
style: selected == x
? widget.selectedTextStyle
: widget.unselectedTextStyle,
// style: TextStyle(
// fontSize: 12,
// fontWeight: selected == x
// ? FontWeight.bold
// : FontWeight.normal),
),
),
),
));
}),
),
),
),
),
!isFocused
? Container()
: ValueListenableBuilder<Offset>(
valueListenable: positionNotifer,
builder:
(BuildContext context, Offset position, Widget? child) {
return Positioned(
right:
widget.alignment == LetterAlignment.right ? 40 : null,
left:
widget.alignment == LetterAlignment.left ? 40 : null,
top: position.dy,
child: widget.overlayWidget == null
? Container()
: widget.overlayWidget!(_filteredAlphabets[
_selectedIndexNotifier.value]));
})
],
);
}
}
class AlphaModel {
final String key;
final String? secondaryKey;
AlphaModel(this.key, {this.secondaryKey});
}
If you want to test my code you can install package linked above, I just changed customization for background of letter scrollview.
You can do Like this :
Check this Example:
Expanded(
child: AlphabetScrollView(
list:
_filterList.map((e) => AlphaModel(e.employeeName)).toList(),
// isAlphabetsFiltered: false,
alignment: LetterAlignment.right,
itemExtent: 90,
unselectedTextStyle: TextStyle(
fontSize: 18,
fontWeight: FontWeight.normal,
color: _filterList.length > 5
? Colors.black
: Colors.transparent),
selectedTextStyle: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
color: _filterList.length > 5
? Colors.red
: Colors.transparent),
overlayWidget: (value) => Stack(
alignment: Alignment.center,
children: [
Icon(
Icons.star,
size: 50,
color: Colors.red,
),
Container(
height: 50,
width: 50,
decoration: BoxDecoration(
shape: BoxShape.circle,
// color: Theme.of(context).primaryColor,
),
alignment: Alignment.center,
child: Text(
'$value'.toUpperCase(),
style: TextStyle(fontSize: 18, color: Colors.white),
),
),
],
),
itemBuilder: (_, index, id) {
return InkWell(
onTap: () => Get.toNamed(EmployeeInfo.route,
arguments: _filterList[index]),
child: Container(
margin: EdgeInsets.only(right: 20),
child: Column(
children: [
Padding(
padding: const EdgeInsets.only(bottom: 12.0),
child: Row(
children: [
SizedBox(
width: 10,
),
Stack(
children: [
CircularProfileAvatar(
SystemConfiguration.baseUrl +
SystemConfiguration.getEmployePhoto +
_filterList[index]
.id, //sets image path, it should be a URL string. default value is empty string, if path is empty it will display only initials
radius: 30, // sets radius, default 50.0
backgroundColor: Colors
.transparent, // sets background color, default Colors.white
borderWidth:
2, // sets border, default 0.0
borderColor: Colors
.blue, // sets border color, default Colors.white
cacheImage:
true, // allow widget to cache image against provided url
imageFit: BoxFit.cover,
// sets on tap
showInitialTextAbovePicture: true,
errorWidget: (BuildContext context,
String data, dynamic v) {
return Icon(
FeatherIcons.user,
size: 40,
color: Colors.white,
);
}, // setting it true will show initials text above profile picture, default false
),
Positioned(
bottom: 5,
right: 0,
child: Padding(
padding:
const EdgeInsets.only(left: 3.0),
child: Container(
width: 17,
height: 17,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: Color(0xFFe0f2f1)),
child: Icon(
Icons.circle,
size: 16.0,
// ignore: unnecessary_null_comparison
color: (_filterList[index]
.attendanceStatus
.length >
0 &&
_filterList[index]
.InOffice
.contains("IN"))
? Colors.green
: Colors.red[500],
),
),
))
],
),
Expanded(
flex: 3,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
mainAxisAlignment:
MainAxisAlignment.start,
crossAxisAlignment:
CrossAxisAlignment.start,
children: [
Text(
_filterList[index].employeeName,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: GoogleFonts.roboto(
fontSize: 13.0,
fontWeight: FontWeight.w500),
),
],
),
),
),
Expanded(
flex: 2,
child: Stack(
children: [
Positioned(
left: 44,
child: ElevatedButton(
onPressed: () => _launchURL(
_filterList[index].mobile1),
child: Icon(
Icons.call,
size: 20.0,
color: Colors.white,
),
style: ElevatedButton.styleFrom(
shape: CircleBorder(),
padding: EdgeInsets.all(2),
primary:
Colors.blue, // <-- Button color
onPrimary:
Colors.red, // <-- Splash color
),
),
),
ElevatedButton(
onPressed: () {
// _textMe(_filterList[index].mobile1); mobile text
Get.toNamed(ChatViwer.route,
arguments: {
"employe_name":
_filterList[index]
.employeeName,
"employe_id":
_filterList[index].id
});
},
child: Icon(
Icons.message,
size: 20.0,
color: Colors.white,
),
style: ElevatedButton.styleFrom(
shape: CircleBorder(),
padding: EdgeInsets.all(2),
primary:
Colors.blue, // <-- Button color
onPrimary:
Colors.red, // <-- Splash color
),
),
],
),
)

Adding next and previous buttons to a ListView in flutter

I was converting the following below ui to code.
I didn't find a suitable package for it, im stepper also didn't have the ability to customize in this way.
So I tried to use listView.builder.
Now I don't know how to add the next and previous buttons.
so that the number inside the scroll view scrolls like the picture below and is placed in the view area.
If you know a suitable package, introduce it.
my code:
FadingEdgeScrollView.fromScrollView(
gradientFractionOnEnd: 0.2,
gradientFractionOnStart: 0.15,
child: ListView.builder(
controller: _controller2,
scrollDirection: Axis.horizontal,
itemBuilder: (BuildContext context, int index) {
int one = index + 1;
int two = 0;
Color colorWhat(int q) {
Color color;
if (q == two) {
color = Color(0xff0AFF6C);
} else {
color = Colors.white;
}
return color;
}
double sizeOfCircle(int qq) {
int size;
if (qq == 0) {
size = 27;
} else {
size = 22;
}
return size.toDouble();
}
double sizeOfCircleText(int qqq) {
double size;
if (qqq < 10) {
size = 13.9;
} else {
size = 13.7;
}
return size;
}
return GestureDetector(
child: Row(
children: [
Container(
alignment: Alignment.center,
width: sizeOfCircle(index),
// height: sizeOfCircle(index),
// padding: EdgeInsets.all(sizeOfCircle(index)),
margin: const EdgeInsets.fromLTRB(
2, 0, 17, 0),
decoration: BoxDecoration(
color: colorWhat(index),
shape: BoxShape.circle,
boxShadow: const [
BoxShadow(
offset: Offset(0, 5),
blurRadius: 10.0,
spreadRadius: -7,
),
],
),
child: Text(
one.toString(),
style: TextStyle(
fontWeight: FontWeight.w600,
fontSize: sizeOfCircleText(index),
),
),
),
],
),
onTap: () =>
Scaffold.of(context).showSnackBar(
SnackBar(
content: Text((index+1).toString()),
),
),
);
},
itemCount: 100,
),
),
first select a current index like this:
int currentPageIndex = 0;
and then on tap function. Write a code like this
for decrement...
if (currentPageIndex == 4) {
return;
}
setState(() {
currentPageIndex += 1;
});
for Increment...
if (currentPageIndex == 4) {
return;
}
setState(() {
currentPageIndex += 1;
});
Change your text...
Text(
'${index + 1}'
style: TextStyle(
fontWeight: FontWeight.w600,
fontSize: sizeOfCircleText(index),
),
),
and change your onTap function like this:
onTap: () {
setState(() {
currentPageIndex = index;
});
},

flutter set selected buttons color dynamically

I have a category bar in my flutter application. Initially all the category names were hard coded but now I'm getting the category names from json data.
what I need to do is to set a green container color for the selected button and the text will be white. And the other buttons will be black text + white container.
When I had hardcoded data I checked the button's index individually like this:
for container:
color: buttonIndex == 0 ? Color(0xff75c760) : Colors.white,
for button text:
buttonIndex == 0 ? Colors.white : Colors.black,
but now the as there can be any number of category name's I dont know how to turn this into a dynamic one.
Full Code:
class _RecipeCategoryBarState extends State<RecipeCategoryBar> {
late AppData appData;
int buttonIndex = 0;
void setIndex(int val) {
setState(() {
buttonIndex = val;
});
}
Widget recipeCategoryButton() {
return Container();
}
#override
void initState() {
super.initState();
appData = Store.instance.getAppData();
}
#override
Widget build(BuildContext context) {
return Container(
height: 50.0,
child: ListView.builder(
scrollDirection: Axis.horizontal,
itemCount: appData.recipeCategories!.length,
itemBuilder: (BuildContext context, int index) {
return ButtonBar(
children: <Widget>[
Material(
color: buttonIndex == 0 ? Color(0xff75c760) : Colors.white,
borderRadius: BorderRadius.circular(15),
clipBehavior: Clip.antiAlias,
child: Padding(
padding: const EdgeInsets.symmetric(
horizontal: 5,
),
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
TextButton(
onPressed: () {
setIndex(0);
widget.sendDataToParent(index);
},
child: Text(
appData.recipeCategories![index].categoryName!,
style: TextStyle(
color:
buttonIndex == 0 ? Colors.white : Colors.black,
fontSize: 12,
fontFamily: "Poppins",
fontWeight: FontWeight.w500,
),
),
),
],
),
),
),
],
);
},
),
);
}
}
If I understand what you want to do, you can achieve it by using the index given by the ListView.builder instead of your buttonIndex variable.

If Else condition problem in onPressed() property in Dart/Flutter

I'm looking to highlight a button from a grid when it's pushed.
Unfortunately, when I do so, the whole column lights up. As I'm new to flutter/Dart and to coding in general, I'm not sure if my problème is my lack of logic or something that I wouldn't know about that coding language?
The home page :
import 'package:flutter/material.dart';
import 'package:sequencer_n_lignes/utilities/sequence_class.dart';
class Home extends StatefulWidget {
#override
_Home createState() => _Home();
}
class _Home extends State<Home> {
int i = 0, y = 0, indexTempo = 0;
int countChanel = 0, countBtn = 0;
bool isPlaying = false;
List<Button> btnList = List();
List<Chanel> chanelList = List();
#override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
backgroundColor: Colors.grey[800],
body: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Container(
margin: EdgeInsets.fromLTRB(20, 10, 20, 10),
decoration: BoxDecoration(
boxShadow: [
BoxShadow(
color: Colors.black,
blurRadius: 5,
spreadRadius: 1,
)
],
color: Colors.grey[900],
border: Border.all(
color: Colors.white,
width: 0.5,
)),
child: Row(
children: <Widget>[
/*__________________________________________ADD/REMOVE BUTTONS___________________*/
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
IconButton(
icon: Icon(Icons.remove),
onPressed: () {
for (i = 0; i < 4; i++) {
btnList.removeLast();
countBtn--;
}
setState(() {});
},
),
Text('BUTTONS: $countBtn'),
IconButton(
icon: Icon(
Icons.add,
),
onPressed: () {
for (i = 0; i < 4; i++) {
btnList.add(Button(
id: countBtn,
onColor: Colors.blue,
offColor: Colors.grey[900],
state: false));
countBtn++;
}
setState(() {});
},
),
],
), //
/*_________________________________________ADD/REMOVE CHANEL___________________*/
Row(
children: <Widget>[
IconButton(
icon: Icon(Icons.remove),
onPressed: () {
chanelList.removeLast();
countChanel--;
setState(() {});
},
),
Text('CHANEL: $countChanel'),
IconButton(
icon: Icon(Icons.add),
onPressed: () {
chanelList.add(
Chanel(id: countChanel, buttonList: btnList));
countChanel++;
setState(() {});
},
),
],
),
SizedBox(
width: 30,
),
/*_____________________________________________CONTROLS___________________*/
Row(
children: <Widget>[
IconButton(
icon: Icon(
Icons.play_arrow,
color: (isPlaying) ? Colors.green : Colors.white,
),
onPressed: () {
if (isPlaying)
isPlaying = false;
else
isPlaying = true;
setState(() {});
},
),
IconButton(
icon: Icon(
Icons.stop,
color: (isPlaying) ? Colors.white : Colors.red[900],
),
onPressed: () {
if (isPlaying)
isPlaying = false;
else
isPlaying = true;
setState(() {});
},
),
IconButton(
icon: Icon(
Icons.refresh,
color: Colors.white,
),
onPressed: () {
for (i = 0; i < chanelList.length; i++) {
for (y = 0; y < btnList.length; y++) {
chanelList[i].buttonList[y].state = false;
}
}
setState(() {});
},
),
RaisedButton.icon(
icon: Icon(
Icons.details,
color: Colors.white,
),
label: Text('OK'),
color: Colors.red[900],
onPressed: () {
setState(() {});
},
)
],
),
],
),
),
/*__________________________________________ GRID ___________________*/
Column(
children: List.generate(countChanel, (indexChanel) {
return Padding(
padding: const EdgeInsets.fromLTRB(0, 5, 0, 5),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: List.generate(countBtn, (indexBtn) {
return Padding(
padding: EdgeInsets.fromLTRB(3, 0, 3, 0),
child: Container(
decoration: BoxDecoration(
boxShadow: [
BoxShadow(
color: Colors.black,
blurRadius: 0.1,
spreadRadius: 0.1,
),
],
border: Border.all(
color: Colors.white,
width: 0.5,
),
),
width: 80,
height: 80,
//THATS WHERE THE PROBLEM IS///////////////////////////
child: FlatButton(
// child: Text(
// '${chanelList[indexChanel].id.toString()} \n${chanelList[indexChanel].buttonList[indexBtn].id.toString()}\n$indexChanel-$indexBtn\n${chanelList[indexChanel].buttonList[indexBtn].state}'),
color: (chanelList[indexChanel]
.buttonList[indexBtn]
.state)
? chanelList[indexChanel]
.buttonList[indexBtn]
.onColor
: chanelList[indexChanel]
.buttonList[indexBtn]
.offColor,
onPressed: () {
if (chanelList[indexChanel]
.buttonList[indexBtn]
.state) {
chanelList[indexChanel]
.buttonList[indexBtn]
.state = false;
} else {
chanelList[indexChanel]
.buttonList[indexBtn]
.state = true;
}
setState(() {});
},
),
),
);
}),
),
);
}),
),
],
),
),
);
}
}
The class
class Button {
int id;
Color onColor = Colors.red[900], offColor = Colors.grey[900];
Color actualColor;
bool state = false;
Button({this.id, this.onColor, this.offColor, this.state});
}
class Chanel {
int id;
List<Button> buttonList;
Chanel({this.id, this.buttonList});
}
Screen shot of the app
Pretty big code but I think the problem is that whenever you add a new Channel, you are giving it an existen buttonList. Try creating a new buttonList when you add a new Channel
chanelList.add(
Chanel(
id: countChanel,
// Here is your problem, the reference to the buttons is the same
// in all channels. Try creating new buttons for every channel
buttonList: btnList,
),
);
I'll go over some of the programming logic improvements 1st and then explain why you are getting unexpected results.
1) Color actualColor inside Button class is never used, remove it.
2) Unless each button is going to have different onColor and offColor I suggest moving those two out of the Button class or at least declare them as static. You are needlessly instantiating them over and over again when I'm guessing you only need those once, this is a very tiny memory improvement (especially since you won't have thousands of buttons) but more importantly removing those from the Button class or making them static will make your code easier to read and understand, as well as cut down the number of arguments needed to pass to the constructor (again cleaner code).
3) Your loop counters "i" and "y", declare them where they are needed. Reduce the scope of the variable so that it is only visible in the scope where it is used. There are many... many reasons for doing so, in a nutshell when a larger scope than necessary is used, code becomes less readable, harder to maintain, and more likely to reference unintended variables.
Now for your actual problem. The problem isn't with if/else statements it has to do with lists and how they are handled in memory.
Going back to my 3rd point above, always use the smallest scope possible.
You are declaring your btnList here
class _Home extends State<Home> {
int i = 0, y = 0, indexTempo = 0;
int countChanel = 0, countBtn = 0;
bool isPlaying = false;
List<Button> btnList = List();
List<Chanel> chanelList = List();
Later on you are adding that same btnList to different Channels here:
Text('CHANEL: $countChanel'),
IconButton(
icon: Icon(Icons.add),
onPressed: () {
chanelList.add(
Chanel(id: countChanel, buttonList: btnList));
countChanel++;
setState(() {});
},
I suggest going back to basics and learn in general about arrays , lists and pointers. You should also search for deep and shallow copying.
What you've done in the code block above is setting the same btnList to all of the chanelList items.
Lets say you create btnList that has 4 items. Lets say you create channelList that has 2 items. Then channelList[ 0 ].buttonList[ 0 ].state will always be the same as channelList[ 1 ].buttonList[ 0 ].state because they are both pointing to the same Button.
To get this:
Quick and easy fix would be to do something like this:
IconButton(
icon: Icon(Icons.add),
onPressed: () {
List<Button> tmpBtnList = new List<Button>();
for(int i=0; i<btnList.length; i++){
tmpBtnList.add(new Button(id: i,state: false));
}
chanelList.add(
Chanel(id: countChanel, buttonList: tmpBtnList));
countChanel++;
setState(() {});
},
),
Complete code on DartPad.
PS I would also refrain from manually counting list items like you've done, just use the the provided .length.