How to make an ExpansionTile trailing Icon spin? - flutter

I've created an expansiontile and I've changed the original trailing icon to an svg icon which I customized. Anytime I click the expansion tile... everything works but the new svg trailing icon doesnt spin like the original trailing icon. Please how do I fix this?
code below
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/svg.dart';
class ExpansionTileCard extends StatelessWidget {
final String toptitle;
final List<Widget> children;
const ExpansionTileCard({Key key, this.toptitle, this.children})
: super(key: key);
#override
Widget build(BuildContext context) {
return Container(
child: new Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
new Container(
decoration: new BoxDecoration(
border:
new Border(bottom: new BorderSide(color: Colors.green))),
child: new Row(
children: [
new Expanded(
flex: 9,
child: new Container(
child: new Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
ExpansionTile(
title: Text(
toptitle,
style: TextStyle(
color: Colors.black,
letterSpacing: -1,
fontSize: 19,
fontWeight: FontWeight.bold),
),
children: children,
childrenPadding: EdgeInsets.fromLTRB(0, 0, 0, 20),
iconColor: Colors.green,
trailing: SvgPicture.asset(
'assets/icons/textmessage.svg',
height: 30,
),
),
],
),
),
)
],
),
),
],
),
);
}
}

While we don't provide trailing on ExpansionTile it uses the default behavior of it.
We are passing a widget into trailing and we need to control it. In order to do that, we need to check if ExpansionTile is expanded or not, the onExpansionChanged call back will provide this value. To handle animation we can use many widgets like Transform, RotatedBox, AnimatedRotation... In this ExpansionTileCard widget will be extended from StatefulWidget .
Steps
convert to StatefulWidget
create a bool inside state class to check the Expanded-state.
bool _isExpanded = false;
change value on onExpansionChanged
onExpansionChanged: (value) {
setState(() {
_isExpanded = value;
});
},
assign your widget on trailing by wrapping animation widget or just check the state return two widget
_isExpanded? ExpandedWidget:NormalViewWidget()
trailing: AnimatedRotation( /// you can use different widget for animation
turns: _isExpanded ? .5 : 0,
duration: Duration(seconds: 1),
child: CustomWidget()// your svgImage here
),
Widget State
class _ExpansionTileCardState extends State<ExpansionTileCard> {
bool _isExpanded = false;
#override
Widget build(BuildContext context) {
return Container(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Container(
decoration: const BoxDecoration(
border: Border(bottom: BorderSide(color: Colors.green))),
child: Row(
children: [
Expanded(
flex: 9,
child: Container(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
ExpansionTile(
onExpansionChanged: (value) {
setState(() {
_isExpanded = value;
});
},
title: Text(
widget.toptitle,
style: TextStyle(
color: Colors.black,
letterSpacing: -1,
fontSize: 19,
fontWeight: FontWeight.bold),
),
children: widget.children,
childrenPadding: EdgeInsets.fromLTRB(0, 0, 0, 20),
iconColor: Colors.green,
trailing: AnimatedRotation(
turns: _isExpanded ? .5 : 0,
duration: Duration(seconds: 1),
child: SvgPicture.asset(
'assets/icons/textmessage.svg',
height: 30,
),
),
],
),
),
)
],
),
),
],
),
);
}
}

Related

Flutter UI challenge

How to create this kind of UI in flutter. with expanded pageview??
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:movie_app/components/bottom_tabs.dart';
import 'package:movie_app/components/card_image.dart';
import 'package:movie_app/components/expandable_text.dart';
import 'package:movie_app/components/heading.dart';
import 'package:movie_app/constants.dart';
import 'package:movie_app/screens/card_details/components/card_details_cast.dart';
import 'package:movie_app/screens/card_details/components/card_details_photos.dart';
import 'package:movie_app/screens/card_details/components/card_details_reviews.dart';
import 'package:movie_app/screens/card_details/components/card_details_tabs.dart';
import '../../../components/my_text.dart';
import '../card_details.dart';
class Body extends StatefulWidget {
const Body({Key key}) : super(key: key);
#override
State<Body> createState() => _BodyState();
}
class _BodyState extends State<Body> {
PageController _tabsPageController;
int _selectedTab = 0;
#override
void initState() {
_tabsPageController = PageController();
super.initState();
}
#override
void dispose() {
_tabsPageController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return SingleChildScrollView(
child: Stack(
clipBehavior: Clip.none,
children: [
Image.network(
"https://static.wikia.nocookie.net/film-vault/images/c/c8/Furypost.jpg/revision/latest?cb=20171202094520",
height: 260,
fit: BoxFit.cover,
),
Positioned(
top: 100,
left: 180,
child: SvgPicture.asset(
"assets/icons/play icon.svg",
color: kwhitecolor,
height: 50,
),
),
Container(
margin: EdgeInsets.only(top: 200, left: 15, right: 15, bottom: 20),
child: Column(
children: [
Row(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
const CardImage(
image:
"https://m.media-amazon.com/images/M/MV5BMjA4MDU0NTUyN15BMl5BanBnXkFtZTgwMzQxMzY4MjE#._V1_.jpg",
width: 120,
),
const SizedBox(width: 15),
// Text("data"),
Column(
// mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: const [
MyText(
"FURY",
fontSize: 22,
isBold: true,
color: Colors.black,
),
SizedBox(width: 2),
MyText(
"(2014)",
fontSize: 14,
isBold: true,
color: Colors.black,
),
],
),
const MyText(
"Action/Drama/War",
fontWeight: FontWeight.w600,
),
const SizedBox(height: 40),
Row(
children: [
const Icon(Icons.remove_red_eye_outlined,
color: klightgreyColor),
const SizedBox(width: 2),
const MyText("1.5M", isBold: true),
const SizedBox(width: 10),
SvgPicture.asset(
"assets/icons/comment.svg",
height: 21,
color: klightgreyColor,
),
const SizedBox(width: 4),
const MyText("1K", isBold: true),
const SizedBox(width: 10),
SvgPicture.asset(
"assets/icons/clock.svg",
height: 18,
color: klightgreyColor,
),
const SizedBox(width: 4),
const MyText("2h 30min", isBold: true),
],
),
],
),
],
),
const SizedBox(height: 30),
const Heading(
"STORYLINE",
fontSize: 18,
padding: 0,
lineColor: Color.fromARGB(255, 255, 68, 68),
// fontWeight: FontWeight.w800,
),
const SizedBox(height: 10),
const ExpandableText(
text:
"1945, the Allies are making their final push in the European theater. A battle-hardened Army sergeant nallier (Brad Pitt), leading a Sherman tank and a five-man crew, undertakes a deadly mission behind enemy lines. Hopelessly outnumbered, outgunned and saddled with an inexperienced soldier (Logan Lerman) in their midst, Wardaddy and his men face overwhelming odds as they move to strike at the heart of Nazi Germany.",
max: 4,
),
SizedBox(height: 10),
CardDetailsBottomTabs(
selectedTab: _selectedTab,
tabPressed: (num) {
_tabsPageController.animateToPage(num,
duration: const Duration(milliseconds: 300),
curve: Curves.easeOutCubic);
},
),
// HERE I WANT TO USE EXPANDED WIDGET INSTEAD OF SIZEDBOX, BUT IT GIVES ME ERROR
SizedBox(
height: 300,
child: PageView(
controller: _tabsPageController,
onPageChanged: (num) {
setState(() {
_selectedTab = num;
});
},
children: const [
CardDetailsCast(),
CardDetailsPhoto(),
CardDetailsReview(),
],
),
),
],
),
),
],
),
);
}
}
this is all what I did so far, when I wrap pageview inside expanded widget it gives me error,
So is there any one who could find a way to wrap the pageview with expanded widget???
this is all what I did so far, when I wrap pageview inside expanded widget it gives me error,
So is there any one who could find a way to wrap the pageview with expanded widget???
this is all what I did so far, when I wrap pageview inside expanded widget it gives me error,
So is there any one who could find a way to wrap the pageview with expanded widget???
You can follow CustomScrollView with SliverAppBar. Example from SliverAppBar
class MyStatefulWidget extends StatefulWidget {
const MyStatefulWidget({Key? key}) : super(key: key);
#override
State<MyStatefulWidget> createState() => _MyStatefulWidgetState();
}
class _MyStatefulWidgetState extends State<MyStatefulWidget> {
bool _pinned = true;
bool _snap = false;
bool _floating = false;
// [SliverAppBar]s are typically used in [CustomScrollView.slivers], which in
// turn can be placed in a [Scaffold.body].
#override
Widget build(BuildContext context) {
return Scaffold(
body: CustomScrollView(
slivers: <Widget>[
SliverAppBar(
pinned: _pinned,
snap: _snap,
floating: _floating,
expandedHeight: 160.0,
flexibleSpace: const FlexibleSpaceBar(
title: Text('SliverAppBar'),
background: FlutterLogo(),
),
),
const SliverToBoxAdapter(
child: SizedBox(
height: 20,
child: Center(
child: Text('Scroll to see the SliverAppBar in effect.'),
),
),
),
SliverList(
delegate: SliverChildBuilderDelegate(
(BuildContext context, int index) {
return Container(
color: index.isOdd ? Colors.white : Colors.black12,
height: 100.0,
child: Center(
child: Text('$index', textScaleFactor: 5),
),
);
},
childCount: 20,
),
),
],
),
bottomNavigationBar: BottomAppBar(
child: Padding(
padding: const EdgeInsets.all(8),
child: OverflowBar(
overflowAlignment: OverflowBarAlignment.center,
children: <Widget>[
Row(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
const Text('pinned'),
Switch(
onChanged: (bool val) {
setState(() {
_pinned = val;
});
},
value: _pinned,
),
],
),
Row(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
const Text('snap'),
Switch(
onChanged: (bool val) {
setState(() {
_snap = val;
// Snapping only applies when the app bar is floating.
_floating = _floating || _snap;
});
},
value: _snap,
),
],
),
Row(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
const Text('floating'),
Switch(
onChanged: (bool val) {
setState(() {
_floating = val;
_snap = _snap && _floating;
});
},
value: _floating,
),
],
),
],
),
),
),
);
}
}

Tab barView doesn't automatically get updated by setstate until the tab bar is clicked on

I am using a Tabbar widget which controls a TabbarView. When I call on setstate after changing the value of a variable named "searchTerm", the anticipated change isn't updated in the tabbarView widgets until I click on any of the tabs.I need the tabviewView to update immediately i call on setstate.Please i need a response as soon as possible See screenshot here
Below is the code
class HomeScreen extends StatefulWidget {
#override
_HomeScreenState createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> with SingleTickerProviderStateMixin {
String searchTerm;
List <Widget> kTabPages;
TabController tabController;
final ktabs = <Tab>[Tab(child: Text("All"),),Tab(child: Text("Popular"),),Tab(child: Text("Top"),),];
#override
void dispose() {
tabController.dispose();
super.dispose();
}
#override
void initState() {
tabController = TabController(length: ktabs.length, vsync: this);
super.initState();
}
#override
Widget build(BuildContext context) {
kTabPages = <Widget>[Center(child: GridWidget(category: "all",searchTerm: searchTerm,),),
Center(child: GridWidget(category: "popular",searchTerm: searchTerm,),),
Center(child: GridWidget(category: "top",searchTerm: searchTerm,),)];
return Scaffold(
resizeToAvoidBottomInset: false,
backgroundColor: Color.fromARGB(255, 69, 22, 99),
body: SafeArea(
child: DefaultTabController(
length: ktabs.length,
child: Column(
children: [
Expanded(child: Container(
child: Padding(
padding: const EdgeInsets.only(left: 25,right: 16,top: 25,bottom: 10),
child: Column(
children: [
Expanded(
flex: 5,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
GestureDetector(
child: Icon(
Icons.apps_outlined,
color: Colors.white,
),
),
GestureDetector(
onTap: ()async{
//assigns a new value to variable searchName after returning from SearchScreen
var searchName = await Navigator.push(context,
MaterialPageRoute(builder: (context){
return SearchScreen();
}));
if(searchName != null){
setState(() {
//Assigns SearchName value to variable searchTerm
searchTerm = searchName;
});
}
},
child: Icon(
Icons.search,
color: Colors.white,
),
),
],
),
SizedBox(
height: 15,
),
Text("Hi Xeroes🖐️",style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.w700,
fontSize: 25.0
),),
SizedBox(
height: 10,
),
Text("Today is a good day",style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.w400,
fontSize: 13.0
),),
SizedBox(
height: 2,
),
Text("To learn something new!",style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.w400,
fontSize: 13.0
),),
],
),
),
Expanded(
flex: 1,
child: TabBar(
controller: tabController,
tabs:ktabs ,
labelColor: Colors.white,
indicatorColor: Colors.amberAccent,
indicatorSize: TabBarIndicatorSize.label,
labelStyle: TextStyle(
// fontSize: 16
),),
)
],
),
),
color: Color.fromARGB(255, 69, 22, 99),
),
flex: 4,),
Expanded(
child: Container(
padding: EdgeInsets.only(top: 20),
child: TabBarView(
controller: tabController,
children: kTabPages,
),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.only(topLeft: Radius.circular(30),topRight:Radius.circular(30))
),
),
flex: 8,)
],
),
),
),
);
}
}

Is it possible to update PopupMenuButton items while it is still open?

Hello to everyone reading this,
So i've been trying to get a PopupMenuButton to change the current selected tile while it is still open. I'm using it in a drawer so a user can easily change their status. I've trimmed my code a bit to provide some basic replicable code. It uses a cubit to manage the users 'status'. The status does update, but the selection in the PopupMenuButton does not. Is there an easy way to fix this?
This is how it looks now:
status change
I've already tried:
Wrapping the child of the PopupMenuItem in a StatefulBuilder and making the PopupMenuButton a stateful widget
Wrapping the child of the PopupMenuItem in a BlocConsumer
Wrapping the child of the PopupMenuItem in a container and giving it a color based the cubit status
Giving the PopupMenuItem a ListTile as a child and setting its selected property based on the cubit status
Making the PopupMenuButton a stateful widget and managing the status using setState
This is my (simplified) code:
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
class DrawerStatusTestScaff extends StatelessWidget {
const DrawerStatusTestScaff({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return BlocProvider(
create: (context) => UserStatusCubit(),
child: Scaffold(
appBar: AppBar(),
drawer: MenuDrawer(),
),
);
}
}
class MenuDrawer extends StatelessWidget {
const MenuDrawer({
Key? key,
}) : super(key: key);
#override
Widget build(BuildContext context) {
final double _headerHeight = kToolbarHeight +
((3 * (kToolbarHeight - 20)) + 40) +
MediaQuery.of(context).padding.top;
void _toSettings() {
print('Settings pressed');
Navigator.pop(context);
}
return Container(
width: 305,
child: Drawer(
child: Column(
children: [
SizedBox(
height: _headerHeight,
child: UserAccountsDrawerHeader(
margin: const EdgeInsets.all(0),
currentAccountPicture: Container(
decoration: BoxDecoration(
shape: BoxShape.circle,
color: const Color(0xFFFFFFFF),
),
child: Center(
child: const Text('S.P',
style: TextStyle(
fontSize: 30,
color: const Color(0xFF03A9F4),
)),
),
),
accountName: const Text('S.O.M.E Person'),
accountEmail: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
StatusPopUp(
child: Row(
children: [
const Text('s.person#companymmail.com'),
Padding(
padding: const EdgeInsets.fromLTRB(15, 2, 0, 0),
child: Icon(
Icons.circle,
color: context.watch<UserStatusCubit>().state == 0
? const Color(0xFF00E676)
: context.watch<UserStatusCubit>().state == 1
? const Color(0xFFFFEE58)
: context
.watch<UserStatusCubit>()
.state ==
2
? const Color(0xFFE53935)
: const Color(0xFFBDBDBD),
size: 10,
),
),
],
),
),
Padding(
padding: const EdgeInsets.only(right: 15),
child: Tooltip(
message: 'Settings',
child: InkWell(
onTap: _toSettings,
child: const Icon(
Icons.settings,
size: 25,
),
),
),
),
],
),
decoration: BoxDecoration(
color: const Color(0xFF03A9F4),
),
),
),
Expanded(
child: ListView(),
),
],
),
),
);
}
}
class StatusPopUp extends StatelessWidget {
final Widget child;
const StatusPopUp({
Key? key,
required this.child,
}) : super(key: key);
#override
Widget build(BuildContext context) {
final int _dropDownIndex = context.watch<UserStatusCubit>().state;
return PopupMenuButton<int>(
tooltip: 'Change Status',
offset: Offset(0, 51.5 + (_dropDownIndex.toDouble() * 48.5)),
onSelected: (int index) {
context.read<UserStatusCubit>().emit(index);
},
initialValue: _dropDownIndex,
child: child,
itemBuilder: (context) => <PopupMenuEntry<int>>[
PopupMenuItem<int>(
value: 0,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const Text('Online'),
Padding(
padding: const EdgeInsets.fromLTRB(15, 2, 0, 0),
child: const Icon(
Icons.circle,
color: const Color(0xFF00E676),
size: 10,
),
),
],
),
),
PopupMenuItem<int>(
value: 1,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const Text('Busy'),
Padding(
padding: const EdgeInsets.fromLTRB(15, 2, 0, 0),
child: const Icon(
Icons.circle,
color: const Color(0xFFFFEE58),
size: 10,
),
),
],
),
),
PopupMenuItem<int>(
value: 2,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const Text('Away'),
Padding(
padding: const EdgeInsets.fromLTRB(15, 2, 0, 0),
child: const Icon(
Icons.circle,
color: const Color(0xFFE53935),
size: 10,
),
),
],
),
),
PopupMenuItem<int>(
value: 3,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const Text('Offline'),
Padding(
padding: const EdgeInsets.fromLTRB(15, 2, 0, 0),
child: const Icon(
Icons.circle,
color: const Color(0xFFBDBDBD),
size: 10,
),
),
],
),
),
],
);
}
}
class UserStatusCubit extends Cubit<int> {
UserStatusCubit() : super(0);
}

Side by side components in flutter

I am trying to make an icon and text in a container side by side by using a row and a column but I haven't been able to get results I want. On the first picture it's my code and on the second it's what I want to achieve. Any help would be app. Also, how do I implement a divider with a different color? Cheers
You are using TextField in your ReusableCard right?
Sure thing you can make it with Row but in this case this is usually done with suffixIcon property:
TextField(
decoration: InputDecoration(
suffixIcon: Icon(Icons.search),
),
Or you can just wrap children widgets that has to be in a row in a, well Row widget. Here is example:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
TextStyle _style = TextStyle(color: Colors.yellow[800], fontSize: 30);
Color _color = Colors.yellow[800];
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Material App',
home: Scaffold(
backgroundColor: Colors.blue[800],
appBar: AppBar(
title: Text('Material App Bar'),
),
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Row(
children: [
Icon(Icons.done, color: _color),
SizedBox(width: 15),
Text('Electricity', style: _style),
Spacer(),
Icon(Icons.done, color: _color),
SizedBox(width: 15)
],
),
Divider(color: _color),
Row(
children: [
Icon(Icons.done, color: _color),
SizedBox(width: 15),
Text('Internet', style: _style),
Spacer(),
Icon(Icons.done, color: _color),
SizedBox(width: 15)
],
),
Divider(),
],
)),
);
}
}
Spacing / alignment of the icons will be tricky without using Stack, but it could be done with lots of fiddling around with padding/margin.
import 'package:flutter/material.dart';
final bool debug = false;
class RowColLayoutPage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Row Col Layout'),
),
body: Column(
children: [
Container(
color: debug ? Colors.lightGreenAccent : null,
child: Row(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Flexible(
flex: 1,
child: RowIcon(icon: Icons.lightbulb)
),
Expanded(
flex: 2,
child: RowText(text: 'Electricity')
)
],
),
),
RowDivider(),
Container(
color: debug ? Colors.lightGreenAccent : null,
child: Row(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Flexible(
flex: 1,
child: RowIcon(icon: Icons.microwave_rounded,)
),
Expanded(
flex: 2,
child: RowText(text: 'Internet')
)
],
),
),
RowDivider(),
],
),
);
}
}
class RowIcon extends StatelessWidget {
final IconData icon;
RowIcon({this.icon});
#override
Widget build(BuildContext context) {
return Container(
alignment: Alignment.centerLeft,
padding: EdgeInsets.only(left: 2),
decoration: BoxDecoration(
border: debug ? Border.all() : null
),
child: Icon(icon, size: 80,)
);
}
}
class RowText extends StatelessWidget {
final String text;
RowText({this.text});
#override
Widget build(BuildContext context) {
return Container(
decoration: BoxDecoration(
border: debug ? Border.all() : null
),
child: Text(text, style: TextStyle(fontSize: 40))
);
}
}
class RowDivider extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Divider(thickness: 1, color: Colors.blue, indent: 15, endIndent: 15,);
}
}

Stateful widget not updating, after being updated in setState, how to solve this?

I am new to Flutter. I am trying to build a Quiz App. Now, I am on the Quiz Screen, and then a quiz has multiple questions. I am showing the question title along with the answers, and when someone clicks on the answer, I am updating the QuestionView again with the new question data. These are stateful widgets, and when the result is fetched I am using setState to update the widget, and if I place a break point there I can see that the things are updated, but that is not rendered on the screen or the view is not changed, it has same title, answers and everything. I am using an optionTap method and you can find it in the comments below. I have mentioned where I am tapping the option and what is done below it.
Here's what I have done so far:
import 'package:flutter/material.dart';
import 'package:flutter_app/Constants/constants.dart';
import 'package:flutter_app/Models/question_model.dart';
import 'package:flutter_app/ViewModels/QuestionsVM.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
QuizQuestionViewModel questionViewModel = QuizQuestionViewModel();
QuizQuestionModel _questionModel;
Widget updateWidget;
class SQQuiz extends StatefulWidget {
final QuizQuestionModel quizQuestionModel;
final int quizId;
SQQuiz({Key key, #required this.quizQuestionModel, #required this.quizId})
: super(key: key);
#override
_SQQuizState createState() =>
_SQQuizState(quizQuestionModel: quizQuestionModel, quizId: quizId);
}
class _SQQuizState extends State<SQQuiz> {
final QuizQuestionModel quizQuestionModel;
final int quizId;
_SQQuizState(
{Key key, #required this.quizQuestionModel, #required this.quizId});
#override
Widget build(BuildContext context) {
_questionModel = quizQuestionModel;
updateWidget = QuestionView(
quizQuestionModel: _questionModel,
quizId: quizId,
);
return Scaffold(
appBar: AppBar(
leading: Container(
child: Row(
children: <Widget>[
IconButton(
onPressed: () {
Navigator.pop(context);
},
icon: Icon(Icons.arrow_back),
),
],
),
),
title: Padding(
padding: const EdgeInsets.symmetric(horizontal: 0),
child: Container(
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Text(
_questionModel.questionDetail.quizName,
style: TextStyle(color: Constants.greyColor, fontSize: 12),
textAlign: TextAlign.left,
),
SizedBox(
width: 14,
),
CircularProgressIndicator(
value: 15,
strokeWidth: 2,
),
],
),
),
),
actions: <Widget>[
Container(
margin: const EdgeInsets.only(right: 10),
child: Center(
child: Container(
child: Text("SCORE ${_questionModel.score}"),
),
),
)
],
),
body: SafeArea(child: updateWidget),
);
}
}
class QuestionView extends StatefulWidget {
final QuizQuestionModel quizQuestionModel;
final int quizId;
QuestionView(
{Key key, #required this.quizQuestionModel, #required this.quizId})
: super(key: key);
#override
_QuestionViewState createState() => _QuestionViewState(
quizQuestionModel: quizQuestionModel,
quizId: quizId,
);
}
class _QuestionViewState extends State<QuestionView> {
final QuizQuestionModel quizQuestionModel;
final int quizId;
_QuestionViewState({#required this.quizQuestionModel, #required this.quizId});
#override
Widget build(BuildContext context) {
QuestionDetail questionDetail = quizQuestionModel.questionDetail;
return Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
mainAxisSize: MainAxisSize.min,
children: <Widget>[
SizedBox(
height: 10,
),
Text(
"Question ${quizQuestionModel.count}/${quizQuestionModel.totalCount}",
style: TextStyle(fontSize: 12),
),
SizedBox(
height: 5,
),
Image(
image: NetworkImage(
questionDetail.pic,
),
fit: BoxFit.cover,
),
Container(
padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 50),
color: Constants.orangeColor,
child: Text(
questionDetail.title,
style: TextStyle(
color: Colors.white,
fontSize: 16,
),
textAlign: TextAlign.center,
),
),
ListView.builder(
itemBuilder: (context, index) {
Answers answers = questionDetail.answers[index];
return Card(
elevation: 5,
margin:
const EdgeInsets.symmetric(vertical: 10, horizontal: 0),
child: ListTile(
onTap: () { //This is where I am tapping the option
optionTap(
context: context,
sessionId: quizQuestionModel.sessionId,
quizId: quizId,
questionId: questionDetail.questionId,
answerId: answers.id,
hintUsed: false,
fiftyUsed: false,
).then((response) {
setState(() { //Here the updateWidget is updated, which you can see in the body, but it is not rendered
_questionModel = response;
updateWidget = new QuestionView(
quizQuestionModel: response,
quizId: quizId,
); // The new QuestionView with new details
});
});
},
contentPadding: const EdgeInsets.symmetric(vertical: 10),
title: Text(
answers.title,
textAlign: TextAlign.center,
),
),
);
},
itemCount: questionDetail.answers.length,
physics: NeverScrollableScrollPhysics(),
shrinkWrap: true,
),
SizedBox(
height: 20,
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
RaisedButton(
padding: const EdgeInsets.symmetric(horizontal: 50),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(5),
),
onPressed: () {
print("50-50 Tapped");
},
child: Text(
"50 | 50\n ${quizQuestionModel.fiftyCoin} coins",
textAlign: TextAlign.center,
style: TextStyle(
color: Colors.white,
),
),
),
Wrap(
spacing: 3,
children: <Widget>[
Icon(FontAwesomeIcons.coins),
Text("${quizQuestionModel.coins}"),
],
),
RaisedButton(
padding: const EdgeInsets.symmetric(horizontal: 50),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(5),
),
onPressed: () {
print("Hint Tapped");
},
child: Text(
"HINT\n ${quizQuestionModel.hintUsed} coins",
textAlign: TextAlign.center,
style: TextStyle(
color: Colors.white,
),
),
)
],
),
],
)
],
);
}
There are no errors at the moment, can anyone please help me with this? Thanks in advance.
No offence - but I think you have completely misunderstood the concept of state management in flutter.
If you have a stateful widget, the setState() method triggers the build() method again. So setState is a notifier to say: Hey there was an update to our variable, please build again.
Your Stateful Widget is doing that. BUT there are no new updates on variables from that widget, because your variables ARE OUTSIDE of the widget. They won't get updated for your StatefulWidget. Consider to rethink you architecture. For small Apps it is enough to pass the variables in a constructor.
Here are some links to get closer to the Flutter-State-Management-Concept:
https://flutter.dev/docs/get-started/codelab
https://flutter.dev/docs/development/data-and-backend/state-mgmt/options