Can I change the initial label text style in syncfusion chart? - flutter

In the Syncfusion chart, text style of axis label can be initially changed depending on the pointIndex of any of the label?
The code I use has a separate widget on the data label. But I can't change the names in XValueMapper.
To make it clearer, below is an example of a piece of code I use:
SfCartesianChart(
title: ChartTitle(
text: title,
backgroundColor: titleBackColor,
textStyle: titleStyle,
),
selectionGesture: ActivationMode.singleTap,
selectionType: SelectionType.point,
series: <ChartSeries>[
StackedBar100Series<RankingChartData, String>(
dataLabelSettings: DataLabelSettings(
builder: (
data,
point,
series,
pointIndex,
seriesIndex,
) {
if (chartData[pointIndex].name.contains(name!)) {
return Padding(
padding: EdgeInsets.only(right: 150.0.w),
child: Icon(
Icons.check_box,
color: primary.base,
),
);
} else {
return const SizedBox();
}
},
isVisible: true,
),
dataSource: chartData,
selectionBehavior: _selectionBehavior,
xValueMapper: (RankingChartData exp, _) => exp.name,
yValueMapper: (RankingChartData exp, _) => exp.positive,
color: positiveColor ?? Colors.green,
sortingOrder: sort ? SortingOrder.ascending : null,
sortFieldValueMapper:
sort ? (RankingChartData exp, _) => exp.positive : null,
onPointTap: (d) {
showDialog(
context: (context),
builder: (context) {
return AlertDialog(
title: Text(chartData[d.pointIndex!].name),
content: Text.rich(TextSpan(children: [
const TextSpan(text: "Positive\n"),
TextSpan(
text: chartData[d.pointIndex!].positive.toString(),
style: TextStyle(
color: positiveColor ?? Colors.green,
fontSize: 20.sp,
),
),
])),
);
});
}),
],
primaryXAxis: CategoryAxis(),
),

Related

Flutter API request is called multiple times

Flutter keeps firing the api request, 200+ x sometimes and this makes the app really slow. I have tried async memorizer already, but this doesn't work. Do you have any idea how I can prevent multiple api requests with this code?
I'm getting an redscreen also when the page is loading. The error says Null check operator used on a null value.
I don't understand what I'm doing wrong. Tips & ideas are welcome.
class ProfileScreen extends StatefulWidget {
// ignore: deprecated_member_use_from_same_package
final ProfileNavigationEnum profileNavigationEnum;
final String? otherUserId;
final String? profileUrl;
final String? coverUrl;
const ProfileScreen({
Key? key,
this.otherUserId,
this.profileUrl,
this.coverUrl,
this.profileNavigationEnum = ProfileNavigationEnum.FROM_FEED,
}) : super(key: key);
#override
_ProfileScreenState createState() => _ProfileScreenState();
}
class _ProfileScreenState extends State<ProfileScreen>
with SingleTickerProviderStateMixin {
ProfileCubit? _profileCubit;
UserPostCubit? userPostCubit;
UserMediaCubit? userMediaCubit;
UserLikesCubit? userLikesCubit;
TabController? tabController;
Size? size;
#override
void initState() {
_profileCubit = BlocProvider.of<ProfileCubit>(context);
tabController = TabController(length: 3, vsync: this);
userPostCubit = getIt<UserPostCubit>();
userMediaCubit = getIt<UserMediaCubit>();
userLikesCubit = getIt<UserLikesCubit>();
_profileCubit!.profileEntity.listen((event) {
userLikesCubit!.userId = event.id;
userMediaCubit!.userId = event.id;
userPostCubit!.userId = event.id;
});
super.initState();
_profileCubit!
.getUserProfile(widget.otherUserId, widget.coverUrl, widget.profileUrl);
}
double textSizePred = 0.0;
#override
Widget build(BuildContext context) {
return Scaffold(
body: BlocConsumer<ProfileCubit, CommonUIState>(
listener: (context, state) => state.maybeWhen(
orElse: () => null,
success: (state) => ProfileListener.success(state, context),
error: (e) => ProfileListener.error(e!, context),
),
builder: (_, state) {
return state.when(
initial: () => LoadingBar(),
success: (s) => getHomeWidget(),
loading: () => LoadingBar(),
error: (e) => Center(
child: NoDataFoundScreen(
onTapButton: context.router.root.pop,
icon: AppIcons.personOption(
color: AppColors.colorPrimary, size: 40),
title: 'Profile Not found',
message: e!.contains("invalid")
? LocaleKeys
.sorry_we_cannot_find_the_page_you_are_looking_for_if_you_still_ne
.tr()
: e,
buttonText: LocaleKeys.go_back.tr(),
),
),
);
},
),
);
}
Widget getHomeWidget() {
return StreamBuilder<ProfileEntity>(
stream: _profileCubit!.profileEntity,
builder: (context, snapshot) {
return DefaultTabController(
length: 3,
child: SafeArea(
child: NestedScrollView(
headerSliverBuilder: (context, value) {
return [
SliverAppBar(
automaticallyImplyLeading: false,
leading: null,
systemOverlayStyle: SystemUiOverlayStyle.light,
elevation: 0.0,
expandedHeight: calculateHeight(
context: context,
item: snapshot.data!,
) as double?,
floating: true,
pinned: true,
actions: [
IconButton(
icon: const Icon(Icons.edit),
onPressed: () async {
await openMediaPicker(
context,
(media) async {
_profileCubit!.changeProfileEntity(
snapshot.data!
.copyWith(backgroundImage: media),
);
await _profileCubit!
.updateProfileCover(media);
},
mediaType: MediaTypeEnum.IMAGE,
allowCropping: true,
);
}).toVisibility(widget.otherUserId == null)
],
// title: Text('Profile'),
backgroundColor: Colors.white,
flexibleSpace: FlexibleSpaceBar(
collapseMode: CollapseMode.pin,
background: snapshot.data == null
? Container()
: TopAppBar(
otherUserId: snapshot.data!.id,
otherUser: widget.otherUserId != null,
profileEntity: snapshot.data,
profileNavigationEnum:
widget.profileNavigationEnum,
onSizeAborted: (size) {
setState(() {
textSizePred = size;
});
},
),
),
// posts,media,likes row
bottom: PreferredSize(
child: Stack(
children: [
Positioned.fill(
child: Container(
decoration: const BoxDecoration(
color: Colors.white,
border: Border(
bottom: BorderSide(
color: Colors.grey,
width: 0.2,
),
),
),
),
),
TabBar(
indicatorWeight: 1,
indicatorSize: TabBarIndicatorSize.label,
labelPadding: const EdgeInsets.all(0),
labelStyle: TextStyle(
fontFamily: 'CeraPro',
fontWeight: FontWeight.w500,
),
unselectedLabelStyle: TextStyle(
fontFamily: 'CeraPro',
fontWeight: FontWeight.bold,
),
tabs: [
Tab(
text: LocaleKeys.posts.tr(),
).toContainer(
alignment: Alignment.center,
decoration: const BoxDecoration(
color: Colors.white,
border: Border(
top: BorderSide(
color: Colors.grey,
width: 0.2,
),
),
),
),
Tab(
text: LocaleKeys.media.tr(),
).toContainer(
alignment: Alignment.center,
decoration: const BoxDecoration(
color: Colors.white,
border: Border(
top: BorderSide(
color: Colors.grey,
width: 0.2,
),
),
),
),
Tab(
text: LocaleKeys.likes.tr(),
).toContainer(
alignment: Alignment.center,
decoration: const BoxDecoration(
color: Colors.white,
border: Border(
top: BorderSide(
color: Colors.grey,
width: 0.2,
),
),
),
),
],
)
],
),
preferredSize: const Size(500, 56),
),
),
];
},
body: TabBarView(
children: [
/// Add the block widget here thhree times
Container(
child: RefreshIndicator(
onRefresh: () {
userPostCubit!.onRefresh();
return Future.value();
},
child: PostPaginationWidget(
isComeHome: false,
isFromProfileSearch: true,
isPrivateAccount: (value) {
_profileCubit!.isPrivateUser = value;
},
isSliverList: false,
noDataFoundScreen: NoDataFoundScreen(
buttonText: LocaleKeys.go_to_the_homepage.tr(),
title: LocaleKeys.no_posts_yet.tr(),
message: "",
onTapButton: () {
context.router.root.pop();
},
),
pagingController: userPostCubit!.pagingController,
onTapLike: userPostCubit!.likeUnlikePost,
onOptionItemTap:
(PostOptionsEnum postOptionsEnum, int index) =>
userPostCubit!.onOptionItemSelected(
context, postOptionsEnum, index),
onTapRepost: userPostCubit!.repost,
),
),
),
Container(
child: RefreshIndicator(
onRefresh: () {
userMediaCubit!.onRefresh();
return Future.value();
},
child: PostPaginationWidget(
isComeHome: false,
isPrivateAccount: (value) {
_profileCubit!.isPrivateUser = value;
},
isSliverList: false,
noDataFoundScreen: NoDataFoundScreen(
title: LocaleKeys.no_media_yet.tr(),
icon: AppIcons.imageIcon(height: 35, width: 35),
buttonText: LocaleKeys.go_to_the_homepage.tr(),
message: "",
onTapButton: () {
context.router.root.pop();
// BlocProvider.of<FeedCubit>(context).changeCurrentPage(ScreenType.home());
// context.router.root.push(Routes.createPost);
},
),
pagingController: userMediaCubit!.pagingController,
onTapLike: userMediaCubit!.likeUnlikePost,
onOptionItemTap: (PostOptionsEnum postOptionsEnum,
int index) async =>
await userMediaCubit!.onOptionItemSelected(
context, postOptionsEnum, index),
onTapRepost: userMediaCubit!.repost,
),
)),
Container(
child: RefreshIndicator(
onRefresh: () {
userLikesCubit!.onRefresh();
return Future.value();
},
child: PostPaginationWidget(
isComeHome: false,
isPrivateAccount: (value) {
_profileCubit!.isPrivateUser = value;
},
isSliverList: false,
noDataFoundScreen: NoDataFoundScreen(
title: LocaleKeys.no_likes_yet.tr(),
icon: AppIcons.likeOption(
size: 35, color: AppColors.colorPrimary),
buttonText: LocaleKeys.go_to_the_homepage.tr(),
message: LocaleKeys
.you_don_t_have_any_favorite_posts_yet_all_posts_that_you_like_wil
.tr(),
onTapButton: () {
context.router.root.pop();
// BlocProvider.of<FeedCubit>(context).changeCurrentPage(ScreenType.home());
// context.router.root.push(Routes.createPost);
},
),
pagingController: userLikesCubit!.pagingController,
onTapLike: userLikesCubit!.likeUnlikePost,
onOptionItemTap:
(PostOptionsEnum postOptionsEnum, int index) =>
userLikesCubit!.onOptionItemSelected(
context,
postOptionsEnum,
index,
),
onTapRepost: userLikesCubit!.repost,
),
)),
],
),
),
),
);
});
}
num calculateHeight({
required BuildContext context,
required ProfileEntity item,
}) {
print('INCHES: ${context.diagonalInches}');
bool isSmallInches = context.diagonalInches <= 4.7;
bool hasWebsite = item.website != null && item.website!.isNotEmpty;
final height = context.getScreenHeight;
final defaultHeight = isSmallInches ? height * .6 : height * .47;
final websiteHeight = hasWebsite ? height * .03 : 0;
final sizeBoxHeight = textSizePred != 0.0 ? 10.h : 0;
return textSizePred + defaultHeight + websiteHeight + sizeBoxHeight;
}
}
/// helps to determine from where user navigated to profile
/// so that on back press of the profile screen we can go back the correct page
/// we're using this because according to the UI we will have the keep the bottom navigation bar under the profile page
enum ProfileNavigationEnum {
FROM_BOOKMARKS,
FROM_FEED,
FROM_SEARCH,
FROM_VIEW_POST,
FROM_MY_PROFILE,
FROM_OTHER_PROFILE,
FROM_MESSAGES,
FROM_NOTIFICATION
} ```

How to change the size of the text in a Pie Chart in flutter?

I have a donut graphic but the texts are too small and I have not been able to make them bigger. it is a "PieChart" chart. I have my data segmented into A, B, C, D, E, F. I need to change text size, I am using a tablet size device and that is why the graph should look large, however the texts do not change size and I do not know how to achieve that my code is as follows:
_RegZonesCircularTabletState createState() => _RegZonesCircularTabletState();
}
class GradesData {
final String gradeSymbol;
final int numberOfStudents;
final charts.Color color;
GradesData(this.gradeSymbol, this.numberOfStudents, this.color);
}
#override
Widget build(BuildContext context) {
// TODO: implement build
throw UnimplementedError();
}
class _RegZonesCircularTabletState extends State<RegZonesCircularTablet> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.green,
centerTitle: true,
title: Text("Región o Zona"),
),drawer: LateralMenu(),
body: ListView(children: <Widget> [
Container(
height: 750,
child:new charts.PieChart(
_getSeriesData(),
animate: true,
defaultRenderer: new charts.ArcRendererConfig(
arcWidth: 200,//ancho de la dona
arcRendererDecorators: [new charts.ArcLabelDecorator(),]
),
),
),
],),
floatingActionButton:Container(
height: 110.0,
width: 110.0,
child: FloatingActionButton(
onPressed: () {
// Add your onPressed code here!
},
child: const Icon(Icons.calendar_today,size: 50),
backgroundColor: Colors.black,
elevation: 5.0,
) ,)
);
}
}
final data = [
GradesData('A', 190,charts.MaterialPalette.indigo.shadeDefault),
GradesData('B', 230,charts.MaterialPalette.purple.shadeDefault),
GradesData('C', 150,charts.MaterialPalette.cyan.shadeDefault),
GradesData('D', 73,charts.MaterialPalette.lime.shadeDefault),
GradesData('E', 31,charts.MaterialPalette.teal.shadeDefault),
GradesData('Fail', 13,charts.MaterialPalette.gray.shadeDefault),
];
_getSeriesData() {
List<charts.Series<GradesData, String>> series = [
charts.Series(
id: "Grades",
data: data,
labelAccessorFn: (GradesData row, _) => '${row.gradeSymbol}: ${row.numberOfStudents}',
domainFn: (GradesData grades, _) => grades.gradeSymbol,
measureFn: (GradesData grades, _) => grades.numberOfStudents,
colorFn: (GradesData grades, _) => grades.color,
)
];
return series;
}
The chart is for tablet
Thanks, I'm a beginner, sorry for the trouble
Using the below code while creating charts.Series, we can manage the text style of the chart
charts.Series<MyModel, String>(
//....
outsideLabelStyleAccessorFn: (_, __) => const charts.TextStyleSpec(
fontSize: 16,
color: charts.MaterialPalette.black,
fontFamily: 'MyFont'
),
insideLabelStyleAccessorFn: (_, __) => const charts.TextStyleSpec(
fontSize: 16,
color: charts.MaterialPalette.white,
fontFamily: 'MyFont'
),
);
And don't forget to add this in PieChart widget
defaultRenderer: charts.ArcRendererConfig(
arcRendererDecorators: [charts.ArcLabelDecorator()],
),

How to get the items from list according to id in flutter?

Requirement:
I created a list of days using CheckboxlistTile, and i want when i check any checkbox a button will display, and onclick on button a dialogue will display where user can add time in textfield, and then on click on submit button of dialogue that textfield time input will convert into a tag and will display below the checkbox.
here my screen look like before check the checkbox
initially i set Monday checkbox checked.
so when i click on add button (which in at the right of checkbox), this dialogue will display
and when i enter the values and after clicking on submit button, tag will look like this
Problem:
Problem is, when i check the tuesday or any other checkbox this tag is displaying in its list, wherease i have not selected time for tuesday or any checkbox, i guess the problem is in list which i'm passing to create tags _timingTagListForToken
here is the code:
Days list class
class CheckBoxListTileModelForToken {
int id;
String title;
bool isCheck;
CheckBoxListTileModelForToken({required this.id,required this.title, required this.isCheck});
static List<CheckBoxListTileModelForToken> getUsers() {
return <CheckBoxListTileModelForToken>[
CheckBoxListTileModelForToken(id:1,title: "Monday", isCheck: true,),
CheckBoxListTileModelForToken(id:2,title: "Tuesday", isCheck: false),
CheckBoxListTileModelForToken(id:3,title: "Wednesday", isCheck: false),
CheckBoxListTileModelForToken(id:4,title: "Thursday", isCheck: false),
CheckBoxListTileModelForToken(id:5,title: "Friday", isCheck: false),
CheckBoxListTileModelForToken(id:6,title: "Saturday", isCheck: false),
CheckBoxListTileModelForToken(id:7,title: "Sunday", isCheck: false),
];
}
}
Code where i'm display the Checkboxes
customExpansionTile(context, "Token Distribution Time",
true,
Icon(Icons.timer, color: HexColor("#5344ed")),
<Widget>[
Container(
child: Row(
children: [
Expanded(
child: SizedBox(
height: MediaQuery.of(context).size.height * 0.45,
child: ListTile(
title: ListView.builder(
itemCount: checkBoxListTileModelForToken.length,
itemBuilder: (BuildContext context, int index) {
return new Card(
child: new Container(
padding: new EdgeInsets.all(10.0),
child: Column(
children: <Widget>[
new CheckboxListTile(
controlAffinity:ListTileControlAffinity.leading,
activeColor: HexColor("#5344ed"),
dense: true,
title: new Text(
checkBoxListTileModelForToken[index].title,
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w600,
letterSpacing: 0.5),
),
value: checkBoxListTileModelForToken[index].isCheck? true:false,
secondary: Container(
alignment:Alignment.centerRight,
height: MediaQuery.of(context).size.height*0.9,
width: MediaQuery.of(context).size.width *0.2,
child:checkBoxListTileModelForToken[index].isCheck ==true?
IconButton(
tooltip:"Pick Time",
onPressed: () {
_tokenTimeDialogue(
checkBoxListTileModelForToken[index].id);
},
icon: Icon(Icons.add,color: HexColor("#5344ed"),)
)
: null),
onChanged: (bool? val) {
itemChangeforToken(val!, index);
}),
SizedBox10(),
Wrap(
direction:Axis.horizontal,
children:[
Container(
child:checkBoxListTileModelForToken[index].isCheck? Tags(
itemCount:_timingTagListForToken.length,
itemBuilder: (int index){
return ItemTags(
key: Key(index.toString()),
activeColor:HexColor("#5344ed"),
index: index,
title:_timingTagListForToken[index],
textStyle: TextStyle( fontSize: 14, ),
combine: ItemTagsCombine.withTextBefore,
removeButton: ItemTagsRemoveButton(
backgroundColor:HexColor("#5344ed"),
onRemoved: (){
setState(() {
_timingTagListForToken.removeAt(index);
});
return true;
},
),
onPressed: (item) => print(item),
onLongPressed: (item) => print(item),
);
},):Padding(
padding: const EdgeInsets.only(left: 70),
child:
Row(crossAxisAlignment: CrossAxisAlignment.center, children: []))
),
])]),
),
);
}),
))),
itemChangeforToken(bool val, int index) {
setState(() {
//id=checkBoxListTileModelForToken[index].id;
//print("id onchange "+ id.toString());
checkBoxListTileModelForToken[index].isCheck = val;
});
}
Dialogue code
_tokenTimeDialogue(dynamic id) {
AlertDialog alert = AlertDialog(
scrollable: true,
insetPadding: EdgeInsets.symmetric(vertical: 50),
title: Text("Add timing of the day",
style: TextStyle(fontWeight: FontWeight.bold, color: HexColor("#5344ed"))),
content: Container(
child: SingleChildScrollView(
scrollDirection: Axis.vertical,
child: Column(children: <Widget>[
textfieldforTimeDialogue(
context,
() async {
TimeOfDay? pickedTime = await showTimePicker(
initialTime: TimeOfDay.now(),
context: context,
builder:(context, child) {
return Theme(
data: Theme.of(context).copyWith(
colorScheme: ColorScheme.light(
primary: HexColor(
"#6610f2"), // header background color
onPrimary: Colors.black, // header text color
onSurface: Colors.black, // body text color
),
textButtonTheme: TextButtonThemeData(
style: TextButton.styleFrom(
primary: HexColor(
"#6610f2"), // button text color
),
),
),
child: child!,
);
},
);
if (pickedTime != null) {
setState(() {
fromTimeForToken.text = pickedTime.format(context);
});
} else {
print("Time is not selected");
}
},
Icons.timer_off,
fromTimeForToken,
"From",
"From",
),
SizedBox20(),
textfieldforTimeDialogue(
context,
() async {
FocusScope.of(context).unfocus();
TimeOfDay? pickedTime = await showTimePicker(
initialTime: TimeOfDay.now(),
context: context,
builder:(context, child) {
return Theme(
data: Theme.of(context).copyWith(
colorScheme: ColorScheme.light(
primary: HexColor(
"#6610f2"), // header background color
onPrimary: Colors.black, // header text color
onSurface: Colors.black, // body text color
),
textButtonTheme: TextButtonThemeData(
style: TextButton.styleFrom(
primary: HexColor(
"#6610f2"), // button text color
),
),
),
child: child!,
);
},
);
if (pickedTime != null) {
setState(() {
toTimeForToken.text = pickedTime.format(context);
});
} else {
print("Time is not selected");
}
},
Icons.timer_off,
toTimeForToken,
"To",
"To",
),
]),
)),
actions: [
TextButton(
onPressed: () {
setState(() {
fromTimeForToken.text="";
toTimeForToken.text="";
});
Navigator.pop(context);
},
child: Text(
"Submit",
style: TextStyle(
fontWeight: FontWeight.bold,
color: HexColor("#5344ed"),
fontSize: 20),
),
)
]);
showDialog(
context: context,
builder: (BuildContext context) {
return alert;
},
);
}
please help where i'm doing wrong, how i can do this?
You are right - you handle the tags list wrong. For every Monday, Tuesday etc., you are looping through the entire list.
Something like this should work (I only changed lines 2 and 3 below)
child:checkBoxListTileModelForToken[index].isCheck? Tags(
itemCount:_timingTagListForToken[index]==null?0:1, //show only a single tag for your option
itemBuilder: (int index2){ // here you must rename your variable, since we need access to index in the outer loop
return ItemTags(
key: Key(index.toString()),
activeColor:HexColor("#5344ed"),
index: index,
title:_timingTagListForToken[index],
textStyle: TextStyle( fontSize: 14, ),
combine: ItemTagsCombine.withTextBefore,
removeButton: ItemTagsRemoveButton(
backgroundColor:HexColor("#5344ed"),
onRemoved: (){
setState(() {
_timingTagListForToken.removeAt(index);
});
return true;
},
),
onPressed: (item) => print(item),
onLongPressed: (item) => print(item),
);

How to add a Linear Progress Indicator once the submit survey button is clicked

I want a linear progress indicator and a circular progress indicator to appear after the user clicks submit survey.
Also, is there a better way to write a survey code without using the survey kits package?
Below is the complete code for the assignment that was used:
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:survey_kit/survey_kit.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: Scaffold(
body: Container(
color: Colors.white,
child: Align(
alignment: Alignment.center,
child: FutureBuilder<Task>(
future: getSampleTask(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done &&
snapshot.hasData &&
snapshot.data != null) {
final task = snapshot.data!;
return SurveyKit(
onResult: (SurveyResult result) {
print(result.finishReason);
},
task: task,
themeData: Theme.of(context).copyWith(
colorScheme: ColorScheme.fromSwatch(
primarySwatch: Colors.cyan,
).copyWith(
onPrimary: Colors.white,
),
primaryColor: Colors.cyan,
backgroundColor: Colors.white,
appBarTheme: const AppBarTheme(
color: Colors.white,
iconTheme: IconThemeData(
color: Colors.cyan,
),
textTheme: TextTheme(
button: TextStyle(
color: Colors.cyan,
),
),
),
iconTheme: const IconThemeData(
color: Colors.cyan,
),
outlinedButtonTheme: OutlinedButtonThemeData(
style: ButtonStyle(
minimumSize: MaterialStateProperty.all(
Size(150.0, 60.0),
),
side: MaterialStateProperty.resolveWith(
(Set<MaterialState> state) {
if (state.contains(MaterialState.disabled)) {
return BorderSide(
color: Colors.grey,
);
}
return BorderSide(
color: Colors.cyan,
);
},
),
shape: MaterialStateProperty.all(
RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8.0),
),
),
textStyle: MaterialStateProperty.resolveWith(
(Set<MaterialState> state) {
if (state.contains(MaterialState.disabled)) {
return Theme.of(context)
.textTheme
.button
?.copyWith(
color: Colors.grey,
);
}
return Theme.of(context)
.textTheme
.button
?.copyWith(
color: Colors.cyan,
);
},
),
),
),
textButtonTheme: TextButtonThemeData(
style: ButtonStyle(
textStyle: MaterialStateProperty.all(
Theme.of(context).textTheme.button?.copyWith(
color: Colors.cyan,
),
),
),
),
),
);
}
return CircularProgressIndicator();
},
),
),
),
),
);
}
Future<Task> getSampleTask() {
var task = NavigableTask(
id: TaskIdentifier(),
steps: [
InstructionStep(
title: 'Dear Customer,\nCongratulations!',
text:
'Simply take this short survey about your experience with us.\n\nClick LET\'S GO! to begin.',
buttonText: 'Let\'s go!',
),
QuestionStep(
title: 'Question 1 out of 7:',
text: 'Which type of shipping do you use most often?',
answerFormat: SingleChoiceAnswerFormat(
textChoices: [
TextChoice(text: 'Letter', value: 'Letter'),
TextChoice(text: 'Parcel', value: 'Parcel'),
TextChoice(text: 'Oversize shipping', value: 'Oversize shipping'),
TextChoice(text: 'None of the above', value: 'None of the above'),
],
),
),
QuestionStep(
title: 'Question 2 out of 7:',
text:
'Do you agree for the Pricing for the type of shipment you prefer?',
answerFormat: SingleChoiceAnswerFormat(
textChoices: [
TextChoice(text: 'Yes', value: 'Yes'),
TextChoice(text: 'No', value: 'No'),
TextChoice(text: 'Not sure', value: 'Not sure'),
],
),
),
QuestionStep(
title: 'Question 3 out of 7:',
text: 'How would you rate our package tracking system?',
answerFormat: SingleChoiceAnswerFormat(
textChoices: [
TextChoice(text: 'Excellent', value: 'Excellent'),
TextChoice(text: 'Good', value: 'Good'),
TextChoice(text: 'Not good', value: 'Not good'),
TextChoice(text: 'Not sure', value: 'Not sure'),
],
),
),
QuestionStep(
title: 'Question 4 out of 7:',
text: 'Have you ever used our complaints department?',
answerFormat: SingleChoiceAnswerFormat(
textChoices: [
TextChoice(text: 'Yes', value: 'Yes'),
TextChoice(text: 'No', value: 'No'),
],
),
),
QuestionStep(
title: 'Question 5 out of 7:',
text: 'Would you consider recommending our shipping/courier service?',
answerFormat: SingleChoiceAnswerFormat(
textChoices: [
TextChoice(text: 'Yes', value: 'Yes'),
TextChoice(text: 'No', value: 'No'),
],
),
),
QuestionStep(
title: 'Question 6 out of 7:',
text: 'Have you ever participated in our survey rewards program?',
answerFormat: SingleChoiceAnswerFormat(
textChoices: [
TextChoice(text: 'Yes', value: 'Yes'),
TextChoice(text: 'No', value: 'No'),
],
),
),
QuestionStep(
title: 'Question 7 out of 7:',
text:
'Would you like to receive notifications about sales and discounts?',
answerFormat: SingleChoiceAnswerFormat(
textChoices: [
TextChoice(text: 'Yes', value: 'Yes'),
TextChoice(text: 'No', value: 'No'),
],
),
),
//This is where the Submit Survey button is
CompletionStep(
stepIdentifier: StepIdentifier(id: '321'),
title: 'Done',
text:
'thanks for taking the survey!',
buttonText: 'Submit survey',
//I want it to take the user to a new page where aminated percent indicators are used
),
],
);
task.addNavigationRule(
forTriggerStepIdentifier: task.steps[7].stepIdentifier,
navigationRule: ConditionalNavigationRule(
resultToStepIdentifierMapper: (input) {
switch (input) {
case "Yes":
return task.steps[0].stepIdentifier;
case "No":
return task.steps[7].stepIdentifier;
default:
return null;
}
},
),
);
return Future.value(task);
}
Future<Task> getJsonTask() async {
final taskJson = await rootBundle.loadString('assets/example_json.json');
final taskMap = json.decode(taskJson);
return Task.fromJson(taskMap);
}
}
Linear progress indicator has been added to the newer versions of the survey_kit. By default the circular progress indicator appears after you submit your survey.

Flutter: Update children state from change in parent

NOTE: The code may seem very long, but for this question you don't need to understand every part of it.
I have an app, which gets data from an API to build a chart with it. I use the Syncfusion cartesian chart package. This is an economic indicator, so it brings a date and a value, for example:
[[2015 Oct, 0.24],[2015 Nov, 0.26],[2015 Dec, 0.32],[2016 Jan, 0.35],[2016 Feb, 0.40],[2016 Mar, 0.48]]
So, once the data arrives (It has a loading screen for waiting the data form the HTTP request), I build the chart with it.
So in this case, my Parent widget is named ChartScreen. Here's the code:
class ChartScreen extends StatefulWidget {
#override
State<ChartScreen> createState() => _ChartScreenState();
}
class _ChartScreenState extends State<ChartScreen> {
String dropdownValue = '';
initState() {
dropdownValue = '2016';
return super.initState();
}
#override
Widget build(BuildContext context) {
final enterpriseProvider = Provider.of<EnterpriseProvider>(context);
final resp = enterpriseProvider.indicator;
List<IpcData> data = _createIpcList(resp, dropdownValue);
if( data.length == 0 ) {
return Scaffold(
appBar: AppBar(
title: Text('Obteniendo datos...'),
),
body: Container(
color: Colors.black,
width: double.infinity,
height: double.infinity,
child: Center(
child: CircularProgressIndicator(),
),
),
);
}
return
Scaffold(
appBar: AppBar(
title: Text('IPC'),
actions:[
Padding(
padding: const EdgeInsets.all(8.0),
child: DropdownButton(
value: dropdownValue,
icon: const Icon(Icons.arrow_downward),
iconSize: 24,
elevation: 16,
style: const TextStyle(color: Colors.white),
underline: Container(
height: 2,
color: Colors.white,
),
onChanged: (String? newValue) {
dropdownValue = newValue!;
data = _createIpcList(resp, dropdownValue);
setState(() {});
},
items: <String>['2016', '2017', '2018', '2019']
.map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
}).toList()
),
)
]
),
drawer: SideMenu(),
body: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Expanded(
child: Container(
child: ResultChart( formattedData: data )###############################
),
),
],
)
);
}
_createIpcList(List<List<dynamic>> resp, [String? year]) {
print('EL AÑOO');
print(year);
List<IpcData>finalList = [];
if(resp.length != 0) {
for(int i = 0; i < resp.length; i++) {
try {
resp[i][0] = DateFormat.yMMM().format(DateTime.parse(resp[i][0]));
} catch(e) {}
}
}
List<IpcData> ipcList = resp.map((e) => IpcData(e[0], e[1])).toList();
if (year!= null) {
for(int i = 0; i < ipcList.length; i++){
if (ipcList[i].date.contains(year)){
finalList.add(ipcList[i]);
}
}
}
return finalList;
}
}
With the _createIpcList I format the JSON data, so the chart can use it. I highlighted the line in which I call the child whose state I want to update. But before that, you can se that I added a dropdown menu, to select a year from a (hardcoded) list. When the dropdown menu selected item changes (see onChanged), I call the SetState and pass the 'year parameter' to the _createIpcList, which filters the data and returns the items that belong to the selected year. Here's the child code:
class ResultChart extends StatefulWidget {
final List<IpcData> formattedData;
const ResultChart({
Key? key,
required this.formattedData
}) : super(key: key);
#override
_ResultChartState createState() => _ResultChartState();
}
class _ResultChartState extends State<ResultChart> {
late List<IpcData> _chartData;
#override
void initState() {
_chartData = widget.formattedData;
super.initState();
}
#override
Widget build(BuildContext context) {
return Container(
child: SfCartesianChart(
backgroundColor: Colors.black,
enableAxisAnimation: false,
trackballBehavior: TrackballBehavior(
enable: true,
shouldAlwaysShow: true,
tooltipSettings: InteractiveTooltip(
borderWidth: 2,
borderColor: Colors.grey,
color: Colors.grey[400],
format: 'point.x : point.y'
)
),
zoomPanBehavior: ZoomPanBehavior(
enablePanning: true,
enablePinching: true,
enableDoubleTapZooming: true,
zoomMode: ZoomMode.xy,
),
primaryXAxis: CategoryAxis(
labelRotation: 90,
labelStyle: TextStyle(
fontWeight: FontWeight.bold,
color: Colors.grey[400]
),
axisLine: AxisLine(
width: 2,
color: Colors.grey
),
majorGridLines: MajorGridLines(width: 1),
),
primaryYAxis: NumericAxis(
labelStyle: TextStyle(
fontWeight: FontWeight.bold,
color: Colors.grey[400]
),
axisLine: AxisLine(
width: 2,
color: Colors.grey
),
title: AxisTitle( text: 'IPC', textStyle: TextStyle(fontSize: 20, fontWeight: FontWeight.bold)),
majorGridLines: MajorGridLines(width: 1),
),
series: <ChartSeries>[
LineSeries<IpcData, String>(
color: Colors.blue,
dataSource: _chartData,
xValueMapper: (IpcData data, _) => data.date,
yValueMapper: (IpcData data, _) => data.value
)
],)
);
}
}
class IpcData {
final String date;
final double value;
IpcData(this.date, this.value);
}
My problem is that, no matter which year I select, the chart doesn't change. I know that the 'dropdownValue' changes because I debugged with some prints() but I don´t know how to rebuild or set state of the ResultChart widget.
Well it turn out that I continued debugging, and actually the ResultChart widget was being rebuilt again and again, but I never called the setState function inside the children. Beginner error I know, but I'm new with Flutter.