How to make ToggleButtons with text under icon - flutter

I'm having a bit of a hard time with this idea.
The goal is to have a row of Toggle Icons with text that can overflow onto a second line.
The issue I'm having with the ToggleButtons is that I can't seem to place text underneath each icon.
I currently have a Map<String, Icon> where the string is the text I want below the Icon from that Map.
Is there an easy/possible way to do this?

Yea, you can achieve this by using the Column widget.
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Icon(Icons.access_alarm),
SizedBox(height: 5.0,),
Text("Text"),
],
);

Please see the following code to put text under icon in a ToggleButton.
import 'package:flutter/material.dart';
final Color darkBlue = const Color.fromARGB(255, 18, 32, 47);
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData.dark().copyWith(scaffoldBackgroundColor: darkBlue),
debugShowCheckedModeBanner: false,
home: Scaffold(
body: Center(
child: MyWidget(),
),
),
);
}
}
class MyWidget extends StatefulWidget {
#override
_MyWidgetState createState() => _MyWidgetState();
}
class _MyWidgetState extends State<MyWidget> {
Map<String, dynamic> map = {
"one": Icons.ac_unit,
"two": Icons.baby_changing_station,
"three": Icons.cached,
"four": Icons.dangerous,
"five": Icons.east,
"six": Icons.face,
};
List<bool> _isSelected = [];
#override
void initState() {
super.initState();
_isSelected = List.filled(map.length, false);
}
#override
Widget build(BuildContext context) {
return Wrap(
children: [
ToggleButtons(
isSelected: _isSelected,
children: [
...map.entries.map((ele) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(ele.value),
Text(ele.key),
],
);
}).toList(),
],
selectedColor: Colors.blueGrey,
onPressed: (value) {
setState(() {
_isSelected = List.filled(map.length, false);
_isSelected[value] = true;
});
},
),
],
);
}
}

I edited up modifying another answers code from another question to use my map
import 'package:flutter/material.dart';
class WrapToggleIconButtons extends StatefulWidget {
const WrapToggleIconButtons({
#required this.symptomIconDataMap,
#required this.isSelected,
#required this.onPressed,
});
final Map<String, IconData> symptomIconDataMap;
final List<bool> isSelected;
final Function onPressed;
#override
_WrapToggleIconButtonsState createState() => _WrapToggleIconButtonsState();
}
class _WrapToggleIconButtonsState extends State<WrapToggleIconButtons> {
int index;
#override
Widget build(BuildContext context) {
final List<String> symptomsList = widget.symptomIconDataMap.keys.toList();
assert(symptomsList.length == widget.isSelected.length);
index = -1;
return Wrap(
children: symptomsList.map((String symptom) {
index++;
return IconToggleButton(
active: widget.isSelected[index],
iconData: widget.symptomIconDataMap[symptom],
text: symptom,
onTap: widget.onPressed,
index: index,
);
}).toList(),
);
}
}
class IconToggleButton extends StatelessWidget {
const IconToggleButton({
#required this.active,
#required this.iconData,
#required this.text,
#required this.onTap,
#required this.index,
this.width,
this.height,
});
final bool active;
final IconData iconData;
final String text;
final Function onTap;
final double width;
final double height;
final int index;
#override
Widget build(BuildContext context) {
return Container(
width: 80.0,
height: height ?? 60.0,
child: Column(
children: [
InkWell(
child: Icon(
iconData,
color: active ? Theme.of(context).accentColor : Theme.of(context).disabledColor,
),
onTap: () => onTap(index),
),
Wrap(
direction: Axis.horizontal,
children: [
Text(
text,
textAlign: TextAlign.center,
),
],
)
],
),
);
}
}
Flutter: Is there a widget to flex toggle buttons

You can also create a custom widget and use it when you need it.
///CustomTextIcon.dart
import 'package:flutter/material.dart';
class MyIconWithText extends StatelessWidget {
final IconData icon;
final String? text;
const MyIconWithText(this.icon, {Key? key,
this.text
}) : super(key: key);
#override
Widget build(BuildContext context) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Icon(icon),
const SizedBox(height: 5.0,),
Text(text ?? ""),
],
);
}
}
and use it as follow:
///Used as a widget
MyIconWithText(Icons.disabled_by_default, text: "Description")

Related

Is there any way to implement swipe onboarding screen in flutter with indicators?

I tried to use two packages carousel_indicator and flutter_onboarding_slider. But my issue is I need to implement indicator like in the image. The selected indicator is a bit big in width. Could anyone help with this?
To show indicators you will have to use another package.
Something like smooth_page_indicator
You can use any of the following packages:
onboarding - Flutter
smooth_page_indicator - Flutter
Add in your pubspec.yaml File and import. You follow the guide for the usage.
You can rely on a 3rd party package such as smooth_page_indicator which was mentionned.
Based on the screenshot you've provided I'd say that the ExpandingDotsEffect is what you're looking for.
Sample
class PageIndicator extends StatelessWidget {
const PageIndicator({
super.key,
required this.length,
required this.controller,
});
final int length;
final PageController controller;
#override
Widget build(BuildContext context) {
return SmoothPageIndicator(
controller: controller,
count: length,
effect: const ExpandingDotsEffect(
// Note that you'll have to change the following properties to match
// your design.
activeDotColor: Colors.orange,
dotColor: Colors.blue,
dotHeight: 8,
dotWidth: 10,
expansionFactor: 3,
),
);
}
}
Screenshot
Full Code
import 'package:flutter/material.dart';
import 'package:smooth_page_indicator/smooth_page_indicator.dart';
class OnboardingPage extends StatefulWidget {
const OnboardingPage({super.key});
#override
State<OnboardingPage> createState() => _OnboardingPageState();
}
class _OnboardingPageState extends State<OnboardingPage> {
final pageController = PageController();
#override
void dispose() {
pageController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
final pages = List<Widget>.generate(
3,
(index) {
return CarouselTile(
title: 'Title $index',
subtitle: 'Subtitle $index',
);
},
);
return Scaffold(
backgroundColor: Colors.grey,
body: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Carousel(
controller: pageController,
pages: pages,
),
PageIndicator(
length: pages.length,
controller: pageController,
),
],
),
),
);
}
}
class Carousel extends StatelessWidget {
const Carousel({
super.key,
required this.controller,
required this.pages,
});
final PageController controller;
final List<Widget> pages;
#override
Widget build(BuildContext context) {
return SizedBox(
height: 200,
child: PageView(
controller: controller,
children: pages,
),
);
}
}
class CarouselTile extends StatelessWidget {
const CarouselTile({
super.key,
required this.title,
required this.subtitle,
});
final String title;
final String subtitle;
#override
Widget build(BuildContext context) {
return Column(
children: [
Text(
title,
style: Theme.of(context).textTheme.headline5,
),
const SizedBox(height: 16),
Text(
subtitle,
style: Theme.of(context).textTheme.subtitle1,
),
],
);
}
}
class PageIndicator extends StatelessWidget {
const PageIndicator({
super.key,
required this.length,
required this.controller,
});
final int length;
final PageController controller;
#override
Widget build(BuildContext context) {
return SmoothPageIndicator(
controller: controller,
count: length,
effect: const ExpandingDotsEffect(
// Note that you'll have to change the following properties to match
// your design.
activeDotColor: Colors.orange,
dotColor: Colors.blue,
dotHeight: 8,
dotWidth: 10,
expansionFactor: 3,
),
);
}
}

How should I apply this code to work with flutter?

class _Page1State extends State<Page1> {
#override
Widget build(BuildContext context) {
var dateSection = Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
DatePickerTimeline(
DateTime.now(),
onDateChange: (date) {
// New date selected
print(date.day.toString());
},
),
],
The Problem is:
I just started coding
Pasting into flutter doesn't work.
How should I apply it to flutter?
What widgets should i put in DatePickerTimeline ?
DatePickerTimeline(
this.currentDate, {
Key key,
this.width,
this.height = 80,
this.monthTextStyle = defaultMonthTextStyle,
this.dayTextStyle = defaultDayTextStyle,
this.dateTextStyle = defaultDateTextStyle,
this.selectionColor = AppColors.defaultSelectionColor,
this.daysCount = 50000,
this.onDateChange,
this.locale,
}) : super(key: key);
);
You can copy paste run full code below
You can declare a variable _selectedValue and set to DateTime.now(), then use it in DatePickerTimeline
code snippet
DateTime _selectedValue = DateTime.now();
...
DatePickerTimeline(
_selectedValue,
onDateChange: (date) {
// New date selected
setState(() {
_selectedValue = date;
});
},
),
working demo
full code
import 'package:date_picker_timeline/date_picker_timeline.dart';
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: Page1(title: 'Date Picker Timeline Demo'),
);
}
}
class Page1 extends StatefulWidget {
Page1({Key key, this.title}) : super(key: key);
final String title;
#override
_Page1State createState() => _Page1State();
}
class _Page1State extends State<Page1> {
DateTime _selectedValue = DateTime.now();
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Container(
padding: EdgeInsets.all(20.0),
color: Colors.blueGrey[100],
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text("You Selected:"),
Padding(
padding: EdgeInsets.all(10),
),
Text(_selectedValue.toString()),
Padding(
padding: EdgeInsets.all(20),
),
DatePickerTimeline(
_selectedValue,
onDateChange: (date) {
// New date selected
setState(() {
_selectedValue = date;
});
},
),
],
),
));
}
}

Flutter: Is there a widget to flex toggle buttons

So I'm the ToggleButtons introduced in 1.9.1 like this:
Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Padding(padding: EdgeInsets.only(top: 8),),
Text('Sort By: '),
Align(
alignment: Alignment.topLeft,
child: ToggleButtons(
borderColor: Color(0xffED7D31),
selectedBorderColor: Color(0xffED7D31),
selectedColor: Color(0xffAD7651),
fillColor: Color(0xffFBE5D6),
color: Color(0xffBF987E),
children: <Widget>[
...state.sortBy.keys.map((name) => Text(name)).toList()
],
onPressed: (index) => state.toggleSortBy(index),
isSelected: state.sortBy.values.toList(),
),
),
],
),
This obviously creates a list of button widgets from a map, the problem I'm facing is that I'm seeing the following warning in my screen
Right overflowed by X pixels
So my question is simply whether there is a way to "flex" the buttons like in CSS, meaning when the number of buttons exceeds the screen size, the buttons would automatically start from the next line.
EDIT:
The state.sortBy is just a Map where I store the texts for my widgets alongside their selected values:
LinkedHashMap<String,bool> sortBy = LinkedHashMap.from({
'Ascending' : true,
'Descending' : false,
})
So this was a bit of work, but I've made a Widget that I've called WrapIconToggleButtons and that should fit what you are looking for. It's rudimentary but you can customize it as you see fit. Please take a look:
How to use (similar to ToggleButtons)
class Main extends StatefulWidget {
#override
_MainState createState() => _MainState();
}
class _MainState extends State<Main> {
List<bool> isSelected = [
false,
false,
false,
false,
false,
false,
false,
false,
];
#override
Widget build(BuildContext context) {
return Center(
child: Container(
child: WrapToggleIconButtons(
iconList: [
Icons.ac_unit,
Icons.shopping_cart,
Icons.shopping_cart,
Icons.done,
Icons.fiber_pin,
Icons.sentiment_satisfied,
Icons.looks_6,
Icons.apps,
],
isSelected: isSelected,
onPressed: (int index) {
setState(() {
for (int buttonIndex = 0; buttonIndex < isSelected.length; buttonIndex++) {
if (buttonIndex == index) {
isSelected[buttonIndex] = !isSelected[buttonIndex];
} else {
isSelected[buttonIndex] = false;
}
}
});
},
),
),
);
}
}
WrapToggleIconButtons Widget
class WrapToggleIconButtons extends StatefulWidget {
final List<IconData> iconList;
final List<bool> isSelected;
final Function onPressed;
WrapToggleIconButtons({
#required this.iconList,
#required this.isSelected,
#required this.onPressed,
});
#override
_WrapToggleIconButtonsState createState() => _WrapToggleIconButtonsState();
}
class _WrapToggleIconButtonsState extends State<WrapToggleIconButtons> {
int index;
#override
Widget build(BuildContext context) {
assert(widget.iconList.length == widget.isSelected.length);
index = -1;
return Wrap(
children: widget.iconList.map((IconData icon){
index++;
return IconToggleButton(
active: widget.isSelected[index],
icon: icon,
onTap: widget.onPressed,
index: index,
);
}).toList(),
);
}
}
class IconToggleButton extends StatelessWidget {
final bool active;
final IconData icon;
final Function onTap;
final int width;
final int height;
final int index;
IconToggleButton({
#required this.active,
#required this.icon,
#required this.onTap,
#required this.index,
this.width,
this.height,
});
#override
Widget build(BuildContext context) {
return Container(
width: width ?? 60,
height: height ?? 60,
child: InkWell(
child: Icon(icon,
color: active ? Theme.of(context).accentColor : Theme.of(context).disabledColor,
),
onTap: () => onTap(index),
),
);
}
}

Flutter: Dissmissible widgets disable Tabview drag detection

I have two tabs, the left tab having a list of tiles and the right tab having nothing. The user can drag the screen from right-to-left or left-to-right to get from one tab to the other.
The left tab has a list of dismissible tiles that only have "direction: DismissDirection.startToEnd" (from left-to-right) enabled so that the user can still theoretically drag (from right-to-left) to go to the right tab.
However, I believe the Dismissible widget still receives the right-to-left drag information which is disabling the TabView drag to change tabs.
In essence, how do I allow the right-to-left drag to be detected by only the TabView and not the Dismissible item?
If an explicit solution/example with code snippets can be given, I would very very much appreciate the help!
Here's a paste for your main.dart file:
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter/semantics.dart';
void main() {
SystemChrome.setPreferredOrientations([
DeviceOrientation.portraitUp,
DeviceOrientation.portraitDown,
]);
runApp(new MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData.dark(),
home: MainPage(),
);
}
}
class MainPage extends StatefulWidget {
#override
State<StatefulWidget> createState() => _MainPageState();
}
class _MainPageState extends State<MainPage>
with SingleTickerProviderStateMixin {
TabController _tabController;
#override
void initState() {
_tabController = TabController(vsync: this, length: 2, initialIndex: 1);
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Container(
color: Colors.black,
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Expanded(
child: TabBarView(
controller: _tabController,
children: <Widget>[
TabWithSomething(),
TabWithNothing(),
],
),
),
],
),
),
),
);
}
}
class TabWithNothing extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Center(
child: Container(
child: Text("Swipe from left-to-right!"),
),
);
}
}
class TabWithSomethingItem implements Comparable<TabWithSomethingItem> {
TabWithSomethingItem({this.index, this.name, this.subject, this.body});
TabWithSomethingItem.from(TabWithSomethingItem item)
: index = item.index,
name = item.name,
subject = item.subject,
body = item.body;
final int index;
final String name;
final String subject;
final String body;
#override
int compareTo(TabWithSomethingItem other) => index.compareTo(other.index);
}
class TabWithSomething extends StatefulWidget {
const TabWithSomething({Key key}) : super(key: key);
static const String routeName = '/material/leave-behind';
#override
TabWithSomethingState createState() => TabWithSomethingState();
}
class TabWithSomethingState extends State<TabWithSomething> {
List<TabWithSomethingItem> TabWithSomethingItems;
void initListItems() {
TabWithSomethingItems =
List<TabWithSomethingItem>.generate(10, (int index) {
return TabWithSomethingItem(
index: index,
name: 'Item $index',
subject: 'Swipe from left-to-right to delete',
body: "Swipe from right-to-left to go back to old tab");
});
}
#override
void initState() {
super.initState();
initListItems();
}
void _handleDelete(TabWithSomethingItem item) {
setState(() {
TabWithSomethingItems.remove(item);
});
}
#override
Widget build(BuildContext context) {
Widget body;
body = ListView(
children:
TabWithSomethingItems.map<Widget>((TabWithSomethingItem item) {
return _TabWithSomethingListItem(
item: item,
onDelete: _handleDelete,
dismissDirection: DismissDirection.startToEnd,
);
}).toList());
return body;
}
}
class _TabWithSomethingListItem extends StatelessWidget {
const _TabWithSomethingListItem({
Key key,
#required this.item,
#required this.onDelete,
#required this.dismissDirection,
}) : super(key: key);
final TabWithSomethingItem item;
final DismissDirection dismissDirection;
final void Function(TabWithSomethingItem) onDelete;
void _handleDelete() {
onDelete(item);
}
#override
Widget build(BuildContext context) {
final ThemeData theme = Theme.of(context);
return Semantics(
customSemanticsActions: <CustomSemanticsAction, VoidCallback>{
const CustomSemanticsAction(label: 'Delete'): _handleDelete,
},
child: Dismissible(
key: ObjectKey(item),
direction: dismissDirection,
onDismissed: (DismissDirection direction) => _handleDelete(),
background: Container(
color: theme.primaryColor,
child: const ListTile(
leading: Icon(Icons.delete, color: Colors.white, size: 36.0))),
child: Container(
decoration: BoxDecoration(
color: theme.canvasColor,
border: Border(bottom: BorderSide(color: theme.dividerColor))),
child: ListTile(
title: Text(item.name),
subtitle: Text('${item.subject}\n${item.body}'),
isThreeLine: true),
),
),
);
}
}
UPDATE:
I'm thinking we could change the "dismissible.dart" file to change the "TabControlller", but i'm not sure how I might do that.
In the "dismissible.dart" file:
...
void _handleDragUpdate(DragUpdateDetails details) {
if (!_isActive || _moveController.isAnimating)
return;
final double delta = details.primaryDelta;
if (delta < 0) print(delta); // thinking of doing something here
...

Handling children taps

I'm beginner on Flutter and I'm trying to create a custom Widget called IconSelect. It should render a list of icons with a legend and the user will choose only one option. When the user taps an icon, it should change the background color of the selected icon and deselect all others.
My first aproach was to create an IconSelect class as a Stateful widget, and another widget called IconSelectItem as Stateless. And the IconSelect would have a children property, containing instances of IconSelectItem.
How can I handle the children taps to change the IconSelect state? Any ideas of others aproaches?
My code:
class IconSelect extends StatefulWidget {
final List<IconSelectItem> children;
final ValueChanged<int> onSaved;
IconSelect({
this.children,
this.onSaved
});
#override
State<StatefulWidget> createState() => new IconSelectState();
}
class IconSelectState extends State<IconSelect> {
int _selectedValue;
_handleTap(int value) {
setState(() {
_selectedValue = value;
});
widget.onSaved(_selectedValue);
}
#override
Widget build(BuildContext context) {
return new Row(
children: widget.children,
);
}
#override
void initState() {
super.initState();
// I tried the code below without success
widget.children.forEach((IconSelectItem item) {
item.onTap = _handleTap(item);
});
}
}
class IconSelectItem extends StatelessWidget {
final Icon icon;
final String legend;
final int value;
VoidCallback onTap;
final bool _selected = false;
IconSelectItem({
Key key,
this.icon,
this.legend,
this.value,
}) : super(key: key);
_handleTap() {
onTap();
}
#override
Widget build(BuildContext context) {
return new GestureDetector(
onTap: () => _handleTap(),
child: new Column(
children: <Widget>[
new CircleAvatar(
radius: 30.0,
child: icon,
backgroundColor: _selected ? Colors.blue : Colors.white,
),
new Center(
child: new Text(legend),
)
],
),
);
}
}
call setState on IconSelectItem's ancestor:
class YourPageState extends State<YourPage> {
int _selectedValue;
#override
Widget build(BuildContext context) {
return new Row(
children: widget.items.map((Item item) {
return new GestureDetector(
onTap: () {
// this class is a ancestor of IconSelectItem.
// setState will rebuild children.
setState(() {
_selectedValue = value;
});
},
child: new IconSelectItem(
icon: item.icon,
legend: item.legend,
value: item.value,
// every time _selectedValue changes,
// IconSelectItem is rebuild by setState.
selected: item.value == _selectedValue,
),
);
}).toList(),
);
}
}
class IconSelectItem extends StatelessWidget {
final Icon icon;
final String legend;
final int value;
final bool selected;
IconSelectItem({
Key key,
this.icon,
this.legend,
this.value,
this.selected = false,
}) : super(key: key);
#override
Widget build(BuildContext context) {
return new Column(
children: <Widget>[
new CircleAvatar(
radius: 30.0,
child: icon,
backgroundColor: selected ? Colors.blue : Colors.white,
),
new Center(
child: new Text(legend),
),
],
);
}
}