I am trying to make my code simple by making it into an array instead I have to write it one by one, but I do not have an idea on how to convert it into an array list. Here for what I have been done. My output is I want to generate the list in a container with scrollable to the right using axis horizontal scroll direction.
class YearSort extends StatelessWidget {
final String title;
const YearSort({
Key? key,
required this.title,
}) : super(key: key);
#override
Widget build(BuildContext context) {
return SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: const [
ByYear(year: 'This year'),
ByYear(year: '2021'),
ByYear(year: '2020'),
ByYear(year: '2019'),
ByYear(year: '2018'),
],
),
);
}
}
class ByYear extends StatefulWidget {
final String year;
const ByYear({
Key? key,
required this.year,
}) : super(key: key);
#override
State<ByYear> createState() => _ByYearState();
}
class _ByYearState extends State<ByYear> {
bool iselected = true;
#override
Widget build(BuildContext context) {
return Container(
width: 100,
height: 30,
decoration: BoxDecoration(
color: iselected
? Color.fromARGB(255, 215, 237, 255)
//Colors.white
: Color.fromARGB(255, 215, 237, 255),
borderRadius: BorderRadius.circular(20),
),
child: Center(child: Text(widget.year, style: const TextStyle(fontFamily: 'Poppins'),)),
);
}
}
Your syntax is kind of not correct for a few widgets and incorrect use of Expanded. This one will help you.
#override
Widget build(BuildContext context) {
return ListView.builder(
itemCount: _title.length,
scrollDirection: Axis.horizontal,
itemBuilder: (context, index) {
return Container(
width: 70,
height: 30,
decoration: BoxDecoration(
color: isSelected
? Colors.yellow
: Colors.blue,
borderRadius: BorderRadius.circular(20),
),
child: Text(_title[index], style: TextStyle(fontFamily: 'Poppins'),),
);
},
);
}
Since you edit the question in middle of nowhere,
class YearSort extends StatefulWidget {
final String title;
const YearSort({
Key? key,
required this.title,
}) : super(key: key);
#override
State<YearSort> createState() => _YearSortState();
}
class _YearSortState extends State<YearSort> {
int selectedIndex = 0;
static List chips = [
DateTime.now().year,
DateTime.now().year - 1,
DateTime.now().year - 2,
DateTime.now().year - 3,
DateTime.now().year - 4
];
#override
Widget build(BuildContext context) {
return Column(
children: [
SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Row(children: [
for(int index = 0; index < chips.length; index++)
Padding(
padding: const EdgeInsets.all(8.0),
child: GestureDetector(
child: ByYear(
year: chips[index].toString(),
isSelected: selectedIndex == index,
),
onTap: () {
selectedIndex = index;
setState(() {});
},
),
)
],),
),
Expanded(child: Container())
],
);
}
}
class ByYear extends StatefulWidget {
final String year;
final bool isSelected;
const ByYear({Key? key, required this.year, this.isSelected = false})
: super(key: key);
#override
State<ByYear> createState() => _ByYearState();
}
class _ByYearState extends State<ByYear> {
#override
Widget build(BuildContext context) {
Color color = widget.isSelected
? const Color.fromARGB(255, 25, 27, 25)
: const Color.fromARGB(255, 215, 237, 255);
Color textColor = !widget.isSelected
? const Color.fromARGB(255, 25, 27, 25)
: const Color.fromARGB(255, 215, 237, 255);
return Container(
height: 30,
width: 100,
decoration: BoxDecoration(
color: color,
borderRadius: BorderRadius.circular(10),
),
child: Center(
child: Text(
widget.year,
style: TextStyle(
fontSize: 16, color: textColor, decoration: TextDecoration.none),
),
),
);
}
}
Note: You can also use Listview.builder instead of singlechildscrollview
If you have an array to show in a scrollable widget, you can use Listview instead of SingleChildScrollView. Like this:
#override
Widget build(BuildContext context) {
return ListView(
scrollDirection: Axis.horizontal,
children: const [
ByYear(year: 'This year'),
ByYear(year: '2021'),
ByYear(year: '2020'),
ByYear(year: '2019'),
ByYear(year: '2018'),
],
shrinkWrap: true,
);
}
hope it helps you.
Related
Well, I am coding a chatbot-like page in my app. But, I am stuck at calling setState function for page inside of chatBubble widget. Here is my page as MedicBot and chat question code as FirstQuestion. What I do want to do that whenever, user triggers radio tile's on tap condition. It should be trigger setState function in MedicBot, any suggestions?
import 'package:medicte/assets/back_button.dart';
import 'package:medicte/assets/first_question.dart';
class MedicBot extends StatefulWidget {
const MedicBot({Key? key}) : super(key: key);
#override
State<MedicBot> createState() => _MedicBotState();
}
class _MedicBotState extends State<MedicBot> {
late final List<Widget> _messages;
late final List<dynamic> botMessages;
FocusNode _focusNode = FocusNode();
setMainState() {
print('bum');
this.setState(() {});
}
#override
void initState() {
print('bumbeyarag');
botMessages = [
_buildChatBubbles(
widget: SizedBox.shrink(),
text:
'Do you have further medical information you can share? (e.g. lab results)',
userControl: false),
_buildChatBubbles(
widget: FirstQuestion(
focus: _focusNode,
radioButtons: ['1-2 weeks', 'A Month', '1-3 Months', 'Other'],
setMainState: setMainState,
),
text: 'Where do you currently live?',
userControl: false),
_buildChatBubbles(
widget: FirstQuestion(
focus: _focusNode,
radioButtons: [
'Online Consultation',
'Second Opinion',
'A treatment cost',
'Other'
],
setMainState: setMainState,
),
text: 'How soon do you want to get the treatment done?',
userControl: false),
_buildChatBubbles(
widget: FirstQuestion(
focus: _focusNode,
radioButtons: ['Yes', 'No'],
setMainState: () {
setState(() {});
},
),
text: 'What service are you looking for?',
userControl: false),
_buildChatBubbles(
widget: FirstQuestion(
focus: _focusNode,
radioButtons: [],
setMainState: () {
setState(() {});
},
),
text: 'Have you already spoken a doctor?',
userControl: false),
_buildChatBubbles(
text: 'Which treatment are you interested in?',
userControl: false,
widget:
const Text('Enter a treatment name (e.g Hair Transplant, IVF)')),
_buildChatBubbles(
text: 'You are inquiring for',
userControl: false,
widget: FirstQuestion(
radioButtons: const ['Myself', 'For someone else'],
focus: _focusNode,
setMainState: () {
setState(() {});
},
)),
];
_messages = [
const SizedBox(
height: 1,
),
const SizedBox(
height: 10,
)
];
super.initState();
}
final TextEditingController _controller = TextEditingController();
bool value = false;
#override
Widget build(BuildContext context) {
if (botMessages.isNotEmpty) {
_messages.insert(1, botMessages.removeLast());
}
return Scaffold(
bottomSheet: Container(
color: Colors.white30,
child: Padding(
padding: const EdgeInsets.only(bottom: 30, right: 15, left: 15),
child: TextFormField(
focusNode: _focusNode,
controller: _controller,
decoration: InputDecoration(
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(25),
),
hintText: 'Type your message',
suffixIcon: IconButton(
onPressed: () {
print(_controller.text);
print(_controller.value);
setState(() {
_messages.insert(
1,
_buildChatBubbles(
text: _controller.text,
userControl: true,
widget: const SizedBox.shrink()));
_controller.clear();
});
},
icon: const Icon(Icons.send),
),
),
),
),
),
appBar: AppBar(
leadingWidth: 101,
backgroundColor: Colors.blue.shade300,
leading: Row(
children: [
const BackWardButton(),
ClipRRect(
borderRadius: BorderRadius.circular(1000),
child: Container(
color: Colors.white,
child: Image.asset(
'lib/images/Lovepik_com-401792159-medical-robot.png',
height: 53,
width: 53),
),
),
],
),
title: const Text(
"MedicBot",
style: TextStyle(color: Colors.black54),
),
),
body: SafeArea(
minimum:
const EdgeInsets.only(top: 2, left: 10, right: 10, bottom: 90),
child: ListView.builder(
itemCount: _messages.length,
reverse: true,
itemBuilder: ((context, index) {
return _messages[index];
}),
)));
}
}
class _buildChatBubbles extends StatelessWidget {
bool userControl;
String text;
Widget widget;
_buildChatBubbles(
{required this.widget,
required this.text,
required this.userControl,
super.key});
#override
Widget build(BuildContext context) {
return Container(
margin: const EdgeInsets.only(bottom: 10),
child: Row(
mainAxisAlignment:
userControl ? MainAxisAlignment.end : MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
userControl
? const SizedBox.shrink()
: Container(
margin: const EdgeInsets.only(right: 10),
child: const CircleAvatar(
radius: 20,
backgroundImage: AssetImage(
'lib/images/Lovepik_com-401792159-medical-robot.png'),
),
),
Container(
constraints: BoxConstraints(
maxHeight: MediaQuery.of(context).size.height * 0.4,
maxWidth: MediaQuery.of(context).size.width * 0.6),
padding: const EdgeInsets.all(10),
decoration: BoxDecoration(
color: userControl ? Colors.green.shade300 : Colors.blue.shade300,
borderRadius: BorderRadius.circular(10),
boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.5),
spreadRadius: 1,
blurRadius: 7,
offset: const Offset(0, 3), // changes position of shadow
),
],
),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
userControl ? 'You' : 'Medicte Bot',
style: const TextStyle(
fontSize: 18,
fontWeight: FontWeight.w600,
),
),
const SizedBox(height: 5),
Flexible(
child: Text(
text,
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.w400,
),
),
),
widget
],
),
),
],
),
);
;
}
}
import 'package:flutter/material.dart';
import 'package:group_button/group_button.dart';
import 'package:medicte/pages/chat_ui.dart';
// ignore: must_be_immutable
class FirstQuestion extends StatefulWidget {
List<String> radioButtons;
FocusNode focus;
void Function() setMainState;
FirstQuestion(
{required this.setMainState,
required this.focus,
required this.radioButtons,
Key? key})
: super(key: key);
#override
State<FirstQuestion> createState() => _FirstQuestionState();
}
class _FirstQuestionState extends State<FirstQuestion> {
late GroupButtonController _radioController;
// ignore: prefer_typing_uninitialized_variables
late final _radioButtons;
#override
void initState() {
_radioButtons = widget.radioButtons;
_radioController = GroupButtonController(
selectedIndexes: [0, 1, 2, 3],
);
super.initState();
}
#override
Widget build(BuildContext context) {
return GroupButton(
controller: _radioController,
isRadio: true,
options: const GroupButtonOptions(groupingType: GroupingType.column),
buttons: _radioButtons,
buttonIndexedBuilder: (selected, index, context) {
return RadioTile(
title: _radioButtons[index],
selected: _radioController.selectedIndex,
index: index,
onTap: () {
print(_radioButtons[index].toString());
widget.setMainState();
_radioController.selectIndex(index);
/* Future.delayed(Duration(seconds: 1), () {
widget.setMainState();
}); */
},
);
},
onSelected: (val, i, selected) {
print('object');
});
}
}
class RadioTile extends StatelessWidget {
const RadioTile({
Key? key,
required this.selected,
required this.onTap,
required this.index,
required this.title,
}) : super(key: key);
final String title;
final int index;
final int? selected;
final VoidCallback onTap;
#override
Widget build(BuildContext context) {
return ListTile(
title: Text(title),
onTap: onTap,
leading: Radio<int>(
groupValue: selected,
value: index,
onChanged: (val) {
print(val);
onTap();
},
),
);
}
}
Try something like this. This is the code snippet of an application of mine. I used StatefulBuilder as the parent of the widgets I want to update and I sent the setState parameter to the widget where I trigger.
import 'package:flutter/material.dart';
class CryptoassetsPage extends StatefulWidget {
const CryptoassetsPage({Key? key}) : super(key: key);
#override
_CryptoassetsPageState createState() => _CryptoassetsPageState();
}
class _CryptoassetsPageState extends State<CryptoassetsPage> {
#override
Widget build(BuildContext context) {
return Container(
color: Theme.of(context).backgroundColor,
child: SingleChildScrollView(
child: StatefulBuilder(
builder: (BuildContext context, StateSetter setState) {
//My other class/widget
return OrderOptions(setState);
}),
),
);
}
}
class OrderOptions extends StatefulWidget {
const OrderOptions(this.setState, {Key? key}) : super(key: key);
final StateSetter setState;
#override
_OrderOptionsState createState() => _OrderOptionsState();
}
class _OrderOptionsState extends State<OrderOptions> {
#override
Widget build(BuildContext context) {
return InkWell(
onTap: () {
StateSetter setState = widget.setState;
setState(() {});
},
);
}
}
I've been trying to implement a similar function like the NavigationBar widget in flutter.
However, I don't want to use Icons instead I wanted to make a custom navbar with desired pics and everything was going well until I couldn't switch the middle section of my app (change different pages) when I tap/press the the textbutton.
You can check the UI here...crappy I know...am mimicking the till from my workplace...so the red section is the part I wanted to update when pressed
The side_buttons.dart file
import 'package:flutter/material.dart';
// ignore: unused_import
import 'package:timtill/main.dart';
class SideButtons extends StatefulWidget {
final String text;
final String imgUrl;
const SideButtons({required this.text, required this.imgUrl});
#override
State<SideButtons> createState() => SideButtonsState();
}
class SideButtonsState extends State<SideButtons> {
//
final List sideBtnLabels = [
'HOT DRINKS',
'COLD DRINKS',
'DONUTS',
'TIMBITS',
'MUFFINS',
'BAGELS',
'SOUP',
'LUNCH',
'BREAK FAST',
'BAKED',
'TAKE-HOME',
'Timmies'
];
#override
Widget build(BuildContext context) {
return Transform.rotate(
angle: -11,
child: Container(
decoration: const BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [Color(0xFF9A9DAD), Color(0xFF4E4C56)])),
height: 80,
width: 80,
child: TextButton(
onPressed: () {
int currentPageIndex = 0;
int index = sideBtnLabels.indexOf(widget.text);
setState(() {
currentPageIndex = index;
});
int navMiddleIndex(int index) {
return index;
}
print(sideBtnLabels.indexOf(widget.text));
// print('index is changed to: ${navMiddleIndex(index).toString()}');
},
//////here Instead of text you can replace Node and import the dart:html
//import 'dart:html';
// text works because in the side_btn_page.dart we have specified the list of menu to it
child: Stack(
alignment: const AlignmentDirectional(0.0, 0.9),
children: [
Image.asset(
'imgs/' + widget.imgUrl,
//imgurl
),
Text(
widget.text, //text
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 14,
foreground: Paint()
..style = PaintingStyle.stroke
..strokeWidth = 3
..color = const Color.fromARGB(255, 63, 63, 63),
),
),
Text(
widget.text, //text
style: const TextStyle(
fontSize: 14,
fontWeight: FontWeight.bold,
color: Color(0xFFEBEBEB),
),
),
],
)),
),
);
}
}
'''
The Main.dart file
Note I wanted to update the currentPageIndex value from zero to the index number When I press the buttons please help me I'm beginner
import 'package:flutter/material.dart';
import 'package:timtill/pages/side_btn_page.dart';
import 'package:timtill/pages/middle_btn_page.dart';
import 'package:timtill/pages/middle_btn_page2.dart';
// ignore: unused_import
import 'package:timtill/util/side_buttons.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return const MaterialApp(
debugShowCheckedModeBanner: false,
title: 'TimsTill',
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key}) : super(key: key);
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int currentPageIndex = 0;
#override
Widget build(BuildContext context) {
return SafeArea(
child: Column(
children: [
SizedBox(height: 80, child: SideButtonPage()),
Expanded(
flex: 12,
child: Container(
color: Colors.red,
child: SizedBox(
width: MediaQuery.of(context).size.width,
child: Padding(
padding: const EdgeInsets.all(8),
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: SingleChildScrollView(
scrollDirection: Axis.vertical,
child: <Widget>[
MiddleButtonPage(),
MiddleButtonPage2(),
Container(
color: Colors.green,
alignment: Alignment.center,
child: const Text('Page 2'),
),
Container(
color: Colors.blue,
alignment: Alignment.center,
child: const Text('Page 3'),
),
][currentPageIndex],
),
),
),
),
)),
Expanded(
flex: 6,
child: Container(
color: Colors.purple,
))
],
),
);
}
}
First of all, you should implement a callback in your SideButtons widget, second, you should implement the defaultPageIndex. This way, SideButtons will return the selected index to its parent widget while maintening its state incase the widget try is rebuilt.
class SideButtons extends StatefulWidget {
final String text;
final String imgUrl;
final int defaultPageIndex;
final ValueChanged<int>? onChanged;
const SideButtons({required this.text, required this.imgUrl, this.defaultPageIndex = 0, this.onChanged});
#override
State<SideButtons> createState() => SideButtonsState();
}
class SideButtonsState extends State<SideButtons> {
//
final List sideBtnLabels = [
'HOT DRINKS',
'COLD DRINKS',
'DONUTS',
'TIMBITS',
'MUFFINS',
'BAGELS',
'SOUP',
'LUNCH',
'BREAK FAST',
'BAKED',
'TAKE-HOME',
'Timmies'
];
late int currentPageIndex;
#override
initState(){
currentPageIndex = defaultPageIndex;
super.initState();
}
#override
Widget build(BuildContext context) {
return Transform.rotate(
angle: -11,
child: Container(
decoration: const BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [Color(0xFF9A9DAD), Color(0xFF4E4C56)])),
height: 80,
width: 80,
child: TextButton(
onPressed: () {
int index = sideBtnLabels.indexOf(widget.text);
setState(() {
currentPageIndex = index;
if( widget.onChanged != null) widget.onChanged(index);
});
int navMiddleIndex(int index) {
return index;
}
print(sideBtnLabels.indexOf(widget.text));
// print('index is changed to: ${navMiddleIndex(index).toString()}');
},
//////here Instead of text you can replace Node and import the dart:html
//import 'dart:html';
// text works because in the side_btn_page.dart we have specified the list of menu to it
child: Stack(
alignment: const AlignmentDirectional(0.0, 0.9),
children: [
Image.asset(
'imgs/' + widget.imgUrl,
//imgurl
),
Text(
widget.text, //text
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 14,
foreground: Paint()
..style = PaintingStyle.stroke
..strokeWidth = 3
..color = const Color.fromARGB(255, 63, 63, 63),
),
),
Text(
widget.text, //text
style: const TextStyle(
fontSize: 14,
fontWeight: FontWeight.bold,
color: Color(0xFFEBEBEB),
),
),
],
)),
),
);
}
}
I am developing a fill-in-the-blanks quiz app.
Each quiz contains 5 sentences and they are arranged by PageView. The sentences are retrieved from a List.
Each sentence is a TextWithBlanks class and they have several BlankWord class (which are TextField).
class TextWithBlanks extends StatefulWidget {
final String text;
static final regex = RegExp("(?={)|(?<=})");
List correctList = [];
TextWithBlanks({Key? key, required this.text, required this.correctList})
: super(key: key);
#override
State<TextWithBlanks> createState() => _TextWithBlanksState();
}
class _TextWithBlanksState extends State<TextWithBlanks> {
#override
Widget build(BuildContext context) {
final split = widget.text.split(TextWithBlanks.regex);
return Padding(
padding: const EdgeInsets.only(top: 30.0, right: 30.0, left: 30.0),
child: Text.rich(
TextSpan(
style: const TextStyle(fontSize: 15, height: 3.0),
children: <InlineSpan>[
for (String text in split)
text.startsWith('{')
? WidgetSpan(
child: blankWord(text.substring(1, text.length - 1),
widget.correctList),
)
: TextSpan(text: text),
],
),
),
);
}
}
class blankWord extends StatefulWidget {
final String answer;
int answerLength = 0;
double answerWidth = 0.0;
String answerHint = "";
List correctList;
String value = "";
bool answerBool = false;
blankWord(this.answer, this.correctList, {Key? key}) : super(key: key) {
answerLength = answer.length;
answerWidth = answerLength * 15.0;
answerHint = answer;
}
#override
State<blankWord> createState() => _blankWordState();
}
class _blankWordState extends State<blankWord> {
Widget build(BuildContext context) {
return SizedBox(
width: widget.answerWidth,
child: TextFormField(
maxLines: null,
cursorColor: Colors.grey,
style: const TextStyle(fontSize: 14, fontWeight: FontWeight.bold),
autofocus: false,
maxLength: widget.answerLength + 5,
onChanged: (enterWord) {
widget.value = enterWord;
if (enterWord == widget.answer) {
if (widget.answerBool == false) {
widget.answerBool = true;
widget.correctList.add(widget.answer);
}
} else {
if (widget.answerBool == true) {
widget.answerBool = false;
widget.correctList.remove(widget.answer);
}
}
},
decoration: InputDecoration(
counterText: "",
hintText: widget.answerHint,
hintStyle: const TextStyle(color: Colors.grey, fontSize: 12),
),
),
);
}
}
If I enter text in one TextWithBlank BlankWord and then enter text in another TextWithBlank BlankWord, what I entered before disappears.
I want to keep the value in the BlankWord (TextField) of each TextWithBlank. What is the best way to do this?
Thank you.
TextWithBlank is included in the QuizText class.
class PlayGame extends StatefulWidget {
final List document;
List correctList = [];
PlayGame({Key? key, required this.document}) : super(key: key);
#override
State<PlayGame> createState() => _PlayGameState();
}
class _PlayGameState extends State<PlayGame> {
int quizNum = 0;
int quizCount = 0;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
leading: Center(
child: Text(
"$quizCount/5",
style: const TextStyle(
fontSize: 25,
fontStyle: FontStyle.italic,
fontWeight: FontWeight.bold),
),
),
actions: [
if (quizCount == 5)
GestureDetector(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => Result(),
),
);
print(widget.correctList.length);
},
child: Row(
children: const [
Text(
"ๆก็นใใ",
style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
),
Icon(
Icons.arrow_forward,
size: 20,
),
SizedBox(
width: 10,
)
],
),
)
else
const SizedBox.shrink()
],
automaticallyImplyLeading: false,
),
body: PageView(
onPageChanged: (counter) {
setState(
() {
quizCount = counter + 1;
},
);
},
children: [
QuizText(widget: widget, quizNum: 0),
QuizText(widget: widget, quizNum: 1),
QuizText(widget: widget, quizNum: 2),
QuizText(widget: widget, quizNum: 3),
QuizText(widget: widget, quizNum: 4)
],
),
);
}
}
class QuizText extends StatelessWidget {
const QuizText({
Key? key,
required this.widget,
required this.quizNum,
}) : super(key: key);
final PlayGame widget;
final int quizNum;
#override
Widget build(BuildContext context) {
return Container(
constraints: const BoxConstraints.expand(),
child: Padding(
padding: const EdgeInsets.all(5.0),
child: Card(
child: SizedBox(
height: double.infinity,
child: SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.only(bottom: 30.0),
child: TextWithBlanks(
text: widget.document[quizNum],
correctList: widget.correctList),
),
),
),
),
),
);
}
}
Once the widget is not used, it disposes from widget tree and value lost. You can use state-management property. For now I am using AutomaticKeepAliveClientMixin to preserve the widget .
Changes will be here
class _TextWithBlanksState extends State<TextWithBlanks>
with AutomaticKeepAliveClientMixin {
#override
Widget build(BuildContext context) {
super.build(context);
final split = widget.text.split(TextWithBlanks.regex);
return Padding(
padding: const EdgeInsets.only(top: 30.0, right: 30.0, left: 30.0),
child: Text.rich(
TextSpan(
style: const TextStyle(fontSize: 15, height: 3.0),
children: <InlineSpan>[
for (String text in split)
WidgetSpan(
child: blankWord(
text.substring(1, text.length - 1),
widget.correctList,
),
)
],
),
),
);
}
#override
bool get wantKeepAlive => true;
}
I want to change the color of the Container when it is pressed and go to the new screen but when I come back the color must have changed.
class FreelancerLayout extends StatefulWidget {
const FreelancerLayout({Key? key}) : super(key: key);
#override
State<FreelancerLayout> createState() => _FreelancerLayoutState();
}
class _FreelancerLayoutState extends State<FreelancerLayout>
with AutomaticKeepAliveClientMixin<FreelancerLayout> {
#override
Widget build(BuildContext context) {
super.build(context);
return SingleChildScrollView(
physics: const BouncingScrollPhysics(),
child: Column(
children: [
Padding(
padding: const EdgeInsets.symmetric(horizontal: 15.0),
child: GridView.builder(
itemCount: catList.length,
shrinkWrap: true,
primary: false,
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3,
crossAxisSpacing: 8.0,
mainAxisSpacing: 10.0),
itemBuilder: (context, index) => Center(
child: GridCategory(
category: catList[index],
press: () {
pushNewScreen(context,
screen: CategoryPage(category: catList[index]));
}),
),
),
),
GridCategory.dart
class GridCategory extends StatefulWidget {
final Category category;
final VoidCallback? press;
const GridCategory({
Key? key,
required this.category,
this.press,
// required this.selectedIcon,
// required this.unSelectedIcon,
}) : super(key: key);
#override
State<GridCategory> createState() => _GridCategoryState();
}
class _GridCategoryState extends State<GridCategory> {
final bool isSelected = false;
#override
Widget build(BuildContext context) {
return InkWell(
onTap: widget.press,
child: Container(
width: 110,
decoration: BoxDecoration(
color: isSelected ? AppColors.fIconsAndTextColor : Colors.white,
borderRadius: BorderRadius.circular(30.0)),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Expanded(child: Image.asset(widget.category.catImage!)),
Text(
widget.category.iconName!,
textAlign: TextAlign.center,
style: TextStyle(color: isSelected ? Colors.white : Colors.black),
),
const SizedBox(height: 10)
],
),
),
);
}
}
First, I don't see any method to change isSelected here, soo it value alway false. Second, isSelected is final make it value can't changes, remove final. Thirt, because isSelected is local props, it changes will no appear in other page, you need to passing selected from parent if you want it.
Update: In case you want highlight selected category when selected and change it when select other cate? You need create a variable call 'selectedIndex' to store the selected category index, handle changes it everytime u tap a category. Update code below
_FreelancerLayoutState
...
int? selectedIndex; // Add this
#override
Widget build(BuildContext context) {
...
itemBuilder: (context, index) => Center(
child: GestureDetector(
onPressed: () {
setState(() => selectedIndex = index);
pushNewScreen(
context,
screen: CategoryPage(category: catList[index])
);
},
child: GridCategory(
category: catList[index],
isSelected: selectedIndex == index,
),
),
),
...
}
}
GridCategory
class GridCategory extends StatefulWidget {
...
final Category category;
final bool isSelected; // add this
// final VoidCallback? press; Remove this
...
}
_GridCategoryState
...
// final bool isSelected = false; Remove this
...
#override
Widget build(BuildContext context) {
// Removed InkWell
// return InkWell(
// onTap: widget.press,
// child: ...
// );
return Container(
width: 110,
decoration: BoxDecoration(
color: widget.isSelected ? AppColors.fIconsAndTextColor : Colors.white,
borderRadius: BorderRadius.circular(30.0)),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Expanded(child: Image.asset(widget.category.catImage!)),
Text(
widget.category.iconName!,
textAlign: TextAlign.center,
style: TextStyle(color: widget.isSelected ? Colors.white : Colors.black),
),
const SizedBox(height: 10)
],
),
),
}
...
Change the GridCategory widget to a stateful widget. Then you can update the onTap function to change the boolean and then call the callback.
onTap: (){
setState((){
isSelected = true;
});
press();
},
On your GridCategory widget, pass isSelected from parent.
class GridCategory extends StatelessWidget {
final Category category;
final VoidCallback? press;
final bool isSelected;
const GridCategory({
Key? key,
required this.isSelected,
required this.category,
this.press,
}) : super(key: key);
......
To keep track of selected index you need a list, I am using List<int> here,
class _FreelancerLayoutState extends State<FreelancerLayout>
with AutomaticKeepAliveClientMixin<FreelancerLayout> {
List<int> selectedIndex = [];
....
itemBuilder: (context, index) => Center(
child: GridCategory(
isSelected: selectedIndex.contains(index),
press: () {
if (selectedIndex.contains(index)) {
selectedIndex.remove(
index); // remove selected index if already exist
} else {
selectedIndex.add(index);
}
//... perform others
}),
I'm trying to create a SideMenu with different SideMenuItems.
For that I created a new class and want to update the color of Text when the SideMenuItem is clicked. For that I want to transfer the activeState and all that stuff you see in the code below:
The use of my class in the Widget:
bool isActive = false;
...
SideMenuItem(
icon: Icon(
Icons.inbox,
size: 20,
color: isActive ? kPrimaryColor : kGrayColor,
),
activeState: isActive,
title: "Archiv",
toggleActiveState: (activeState) {
setState(() {
isActive = !activeState;
});
},
),
And here is my class:
import 'package:flutter/material.dart';
import 'package:gastronomy/constants.dart';
class SideMenuItem extends StatelessWidget {
// ignore: prefer_const_constructors_in_immutables
SideMenuItem({
Key? key,
required this.activeState,
this.itemCount = 0,
this.showBorder = true,
#required this.icon,
#required this.title,
required this.toggleActiveState,
}) : super(key: key);
final bool activeState;
final bool showBorder;
final int itemCount;
final Icon? icon;
final String? title;
final Function(bool) toggleActiveState;
#override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.only(top: kDefaultPadding),
child: InkWell(
onTap: () {
toggleActiveState(activeState);
},
child: Row(
children: [
const SizedBox(width: 15),
const SizedBox(width: kDefaultPadding / 4),
Expanded(
child: Container(
padding: const EdgeInsets.only(bottom: 15, right: 5),
decoration: showBorder
? const BoxDecoration(
border: Border(
bottom: BorderSide(color: Color(0xFFDFE2EF)),
),
)
: null,
child: Row(
children: [
icon!,
const SizedBox(width: kDefaultPadding * 0.75),
Text(
title!,
style: Theme.of(context).textTheme.button?.copyWith(
color: activeState ? kTextColor : kGrayColor,
),
),
const Spacer(),
// if (itemCount != null) CounterBadge(count: itemCount)
],
),
),
),
],
),
),
);
}
}
I ended up with that pieces of code but well, how you might know, all SideMenuItems change there color when I click one.
I'm pretty new at using this way of code so I would be thankful to all informations you can include into your answer.
One option is to render all the menu items through a map function and compare each item with the selected option, like in the example below:
import 'package:flutter/material.dart';
class MenuExample extends StatefulWidget {
const MenuExample({Key? key}) : super(key: key);
#override
_MenuExampleState createState() => _MenuExampleState();
}
class _MenuExampleState extends State<MenuExample> {
List<String> menuOptions = const ['Item 1', 'Item 2', 'Item 3'];
String selectedOption = '';
#override
Widget build(BuildContext context) {
return Scaffold(
drawer: Drawer(
backgroundColor: Colors.amber,
child: ListView(
children: menuOptions.map((menuOption) {
return InkWell(
onTap: () => setState(() {
selectedOption = menuOption;
}),
child: MenuItem(
name: menuOption,
isSelected: menuOption == selectedOption,
),
);
}).toList()),
),
);
}
}
class MenuItem extends StatelessWidget {
const MenuItem({Key? key, this.isSelected = false, required this.name})
: super(key: key);
final bool isSelected;
final String name;
#override
Widget build(BuildContext context) {
return ListTile(
title: Text(
name,
style: TextStyle(
fontWeight: isSelected ? FontWeight.bold : FontWeight.normal),
),
);
}
}