Flutter stateful widget with child not updating state - android

I'm working on a part of an app that will essentially just be keeping track of physical tokens that are like forms of currency. I'm trying to build a reusable Widget that will take in the state of that token quantity as a parameter, and increment/decrement that based on user interaction. For the sake of clarity, I've just included the decrement part of the Widget. My question: is the state of the token that is getting passed into the widget not updating because it's just a reference to that state? Or am I missing something else.
class RedeemTokensState extends State<RedeemTokens> {
int oneQuantity = 0;
int fiveQuantity = 0;
int tenQuantity = 0;
int total = 0;
Widget _counterWidget(int tokenQuantity) {
return Row(
children: <Widget>[
Expanded(
child: IconButton(
icon: Icon(Icons.remove),
onPressed: () {
setState(() {
tokenQuantity = tokenQuantity - 1;
print(tokenQuantity);
});
},
),
),
),
}
Widget _buildOneField() {
return ListTile(
title: Text('\$1 Token'),
trailing: Container(width: 200.0, child: _counterWidget(oneQuantity)),
);
}
Widget _buildFiveField() {
return ListTile(
title: Text('\$5 Token'),
trailing: Container(width: 200.0, child: _counterWidget(fiveQuantity)),
);
}
Widget _buildTenField() {
return ListTile(
title: Text('\$10 Token'),
trailing: Container(width: 200.0, child: _counterWidget(tenQuantity)),
);
}
}
// main scaffold with build method
... Card(
child: Container(
padding: EdgeInsets.all(10.0),
child: Column(
children: <Widget>[
_buildOneField(),
Divider(),
_buildFiveField(),
Divider(),
_buildTenField(),
Divider(),
_buildFreshConnectField(),
],
),
),
),

A generic solution could look like:
Parent widget
class RedeemTokens extends StatefulWidget {
#override
RedeemTokensState createState() => RedeemTokensState();
}
class RedeemTokensState extends State<RedeemTokens> {
final _quantities = new Map<TokenType, int>.fromIterable(TokenType.values,
key: (k) => k, value: (k) => 0);
Widget build(BuildContext context) {
final widgets = <Widget>[];
for (final type in _quantities.keys) {
widgets
..add(
new TokenQuantity(
tokenType: type,
quantity: _quantities[type],
onQuantityUpdated: (newValue) {
setState(() {
print('\$${type.value}: $newValue');
print(_quantities);
_quantities[type] = newValue;
});
}),
)
..add(Divider());
}
// widgets.add(_buildFreshConnectField());
return Card(
child: Container(
padding: EdgeInsets.all(10.0),
child: Column(
children: widgets,
),
),
);
}
}
Child widget added once per TokenType
class TokenQuantity extends StatelessWidget {
const TokenQuantity(
{#required this.tokenType,
#required this.quantity,
this.onQuantityUpdated})
: assert(quantity != null);
final TokenType tokenType;
final int quantity;
final TokenQuantityUpdatedFn onQuantityUpdated;
Widget _counterWidget() {
return Row(
children: <Widget>[
Text('$quantity'),
Expanded(
child: IconButton(
icon: Icon(Icons.remove),
onPressed: () {
if (onQuantityUpdated != null) {
onQuantityUpdated(quantity - 1);
}
},
),
),
],
);
}
#override
Widget build(BuildContext context) {
return ListTile(
title: Text('\$${tokenType.value} Token'),
trailing: Container(width: 200.0, child: _counterWidget()),
);
}
}
Typedef for the event callback
typedef TokenQuantityUpdatedFn = void Function(int newValue);
"Old-style" enum to be able to set custom values.
class TokenType {
static const one = const TokenType(1);
static const fife = const TokenType(5);
static const ten = const TokenType(10);
static const values = const <TokenType>[one, fife, ten];
final int value;
const TokenType(this.value);
#override
String toString() => 'TokenType $\$value';
}

Related

Is there a way to have a "User" place a button widget on an interactive viewer. (Like a Floorplan Builder)

I am building an app that allows the user to build their gyms floorplan and place buttons where the machines would be.
I am currently using an Interactive Viewer to display the floorplan. I need the functionality to create a button widget where the "User" long-presses.
Here is my Current Code with the onLongPressEnd class.
Padding(
padding: const EdgeInsets.symmetric(vertical: 1),
child: Center(
child: GestureDetector(
onLongPressEnd: ((details) => {
//Code to create button goes here
}),
child: InteractiveViewer(
minScale: 1,
maxScale: 2,
child: Stack(
children: [
Image.asset(
'Assets/Lift View Images/room_layout_1.png'),
);
Someone on the internet has posted an almost identical problem to this and created a whole solution, the problem is since his solution, the InteractiveViewer widget has been added so all of his code is obsolete and cannot be copied to a newer version.
https://medium.com/#alexander.arendar/dragging-zooming-and-placing-object-on-indoor-map-with-flutter-67667ef415ec
In conclusion I need the functionality for the user to create pre defined widgets by pressing on the page.
Full Code https://github.com/CokeMango/mapview_iteration_1.git
I tried for hours to understand this documents solution, https://medium.com/#alexander.arendar/dragging-zooming-and-placing-object-on-indoor-map-with-flutter-67667ef415ec but never figured out how to implement it with the Interactive Viewer widget. And being fairly new to Flutter I couldn't replicate it exactly.
I also searched online for a while and at most I found what I already had which was a zoomable scrollable image viewer with no functionality.
here you can see two approaches: FloorPlanWithFixedButtons adds "buttons" with a fixed size (no matter what the current zoom factor is used by InteractiveViewer), while FloorPlanWithScaledButtons adds "buttons" directly to InteractiveViewer so they are automatically scaled when you zoom-in / zoom-out
if you need draggable "buttons" check FloorPlanWithScaledDraggableButtons widget
class ChipEntry {
ChipEntry({
required this.offset,
required this.label,
});
Offset offset;
final String label;
}
FloorPlanWithFixedButtons
class FloorPlanWithFixedButtons extends StatefulWidget {
#override
State<FloorPlanWithFixedButtons> createState() => _FloorPlanWithFixedButtonsState();
}
class _FloorPlanWithFixedButtonsState extends State<FloorPlanWithFixedButtons> with TickerProviderStateMixin {
int animatedIndex = -1;
late final controller = AnimationController(vsync: this, duration: const Duration(milliseconds: 250));
final chips = <ChipEntry>[];
final transformationController = TransformationController();
int labelNumber = 0;
#override
Widget build(BuildContext context) {
return Column(
children: [
const Padding(
padding: EdgeInsets.all(8.0),
child: Text('1) long press on the floor plan below to add a new button\n'),
),
Expanded(
child: ClipRect(
child: GestureDetector(
onLongPressStart: _addButton,
child: Stack(
children: [
InteractiveViewer(
minScale: 1,
maxScale: 5,
constrained: false,
transformationController: transformationController,
// https://upload.wikimedia.org/wikipedia/commons/thumb/9/9a/Sample_Floorplan.jpg/640px-Sample_Floorplan.jpg
child: Image.asset('images/640px-Sample_Floorplan.jpg'),
),
CustomMultiChildLayout(
delegate: FloorPlanDelegate(
chips: chips,
transformationController: transformationController,
),
children: [
for (int index = 0; index < chips.length; index++)
LayoutId(id: index, child: _button(index)),
],
),
],
),
),
),
),
],
);
}
Widget _button(int index) {
final button = Chip(
backgroundColor: Colors.orange,
side: const BorderSide(width: 1, color: Colors.black12),
elevation: 4,
onDeleted: () async {
setState(() {
animatedIndex = index;
});
await controller.reverse(from: 1.0);
setState(() {
chips.removeAt(index);
animatedIndex = -1;
});
},
label: InkWell(
onTap: () => print('button |${chips[index].label}| at index $index pressed'),
child: Text(chips[index].label),
),
);
return index == animatedIndex? ScaleTransition(scale: controller, child: button) : button;
}
void _addButton(LongPressStartDetails details) async {
setState(() {
animatedIndex = chips.length;
final chipEntry = ChipEntry(
offset: transformationController.toScene(details.localPosition),
label: 'btn #$labelNumber'
);
chips.add(chipEntry);
labelNumber++;
});
await controller.forward(from: 0.0);
animatedIndex = -1;
}
}
class FloorPlanDelegate extends MultiChildLayoutDelegate {
FloorPlanDelegate({
required this.chips,
required this.transformationController,
}) : super(relayout: transformationController); // NOTE: this is very important
final List<ChipEntry> chips;
final TransformationController transformationController;
#override
void performLayout(ui.Size size) {
// print('performLayout $size');
int id = 0;
final constraints = BoxConstraints.loose(size);
final matrix = transformationController.value;
for (final chip in chips) {
final size = layoutChild(id, constraints);
final offset = MatrixUtils.transformPoint(matrix, chip.offset) - size.center(Offset.zero);
positionChild(id, offset);
id++;
}
}
#override
bool shouldRelayout(covariant MultiChildLayoutDelegate oldDelegate) => false;
}
FloorPlanWithScaledButtons
class FloorPlanWithScaledButtons extends StatefulWidget {
#override
State<FloorPlanWithScaledButtons> createState() => _FloorPlanWithScaledButtonsState();
}
class _FloorPlanWithScaledButtonsState extends State<FloorPlanWithScaledButtons> with TickerProviderStateMixin {
int animatedIndex = -1;
late final controller = AnimationController(vsync: this, duration: const Duration(milliseconds: 250));
final chips = <ChipEntry>[];
final transformationController = TransformationController();
int labelNumber = 0;
#override
Widget build(BuildContext context) {
return Column(
children: [
const Padding(
padding: EdgeInsets.all(8.0),
child: Text('1) long press on the floor plan below to add a new button\n'),
),
Expanded(
child: ClipRect(
child: GestureDetector(
onLongPressStart: _addButton,
child: InteractiveViewer(
minScale: 1,
maxScale: 5,
constrained: false,
transformationController: transformationController,
child: Stack(
children: [
// https://upload.wikimedia.org/wikipedia/commons/thumb/9/9a/Sample_Floorplan.jpg/640px-Sample_Floorplan.jpg
Image.asset('images/640px-Sample_Floorplan.jpg'),
...chips.mapIndexed(_positionedButton),
],
),
),
),
),
),
],
);
}
Widget _positionedButton(int index, ChipEntry chip) {
final child = Chip(
backgroundColor: Colors.orange,
side: const BorderSide(width: 1, color: Colors.black12),
elevation: 4,
onDeleted: () async {
setState(() {
animatedIndex = index;
});
await controller.reverse(from: 1.0);
setState(() {
chips.removeAt(index);
animatedIndex = -1;
});
},
label: InkWell(
onTap: () => print('button |${chip.label}| at index $index pressed'),
child: Text(chip.label),
),
);
return Positioned(
left: chip.offset.dx,
top: chip.offset.dy,
child: FractionalTranslation(
translation: const Offset(-0.5, -0.5),
child: index == animatedIndex? ScaleTransition(scale: controller, child: child) : child,
),
);
}
void _addButton(LongPressStartDetails details) async {
setState(() {
animatedIndex = chips.length;
final chipEntry = ChipEntry(
offset: transformationController.toScene(details.localPosition),
label: 'btn #$labelNumber'
);
chips.add(chipEntry);
labelNumber++;
});
await controller.forward(from: 0.0);
animatedIndex = -1;
}
}
FloorPlanWithScaledDraggableButtons
class FloorPlanWithScaledDraggableButtons extends StatefulWidget {
#override
State<FloorPlanWithScaledDraggableButtons> createState() => _FloorPlanWithScaledDraggableButtonsState();
}
class _FloorPlanWithScaledDraggableButtonsState extends State<FloorPlanWithScaledDraggableButtons> {
final chips = <ChipEntry>[];
final transformationController = TransformationController();
int labelNumber = 0;
#override
Widget build(BuildContext context) {
return Column(
children: [
const Padding(
padding: EdgeInsets.all(8.0),
child: Text('1) long press on the floor plan below to add a new button\n'
'2) long press on the added button to drag it'),
),
Expanded(
child: ClipRect(
child: GestureDetector(
onLongPressStart: _addButton,
child: InteractiveViewer(
minScale: 1,
maxScale: 5,
constrained: false,
transformationController: transformationController,
child: Stack(
children: [
// https://upload.wikimedia.org/wikipedia/commons/thumb/9/9a/Sample_Floorplan.jpg/640px-Sample_Floorplan.jpg
Image.asset('images/640px-Sample_Floorplan.jpg'),
...chips.mapIndexed(_button),
],
),
),
),
),
),
],
);
}
Widget _button(int index, ChipEntry chip) {
return DraggableChip(
chip: chip,
onTap: () => print('button |${chip.label}| at index $index pressed'),
onDrag: (delta) => setState(() => chip.offset += _scaled(delta)),
onDeleted: () => setState(() => chips.removeAt(index)),
);
}
Offset _scaled(Offset delta) {
return delta / transformationController.value.getMaxScaleOnAxis();
}
void _addButton(LongPressStartDetails details) {
setState(() {
final chipEntry = ChipEntry(
offset: transformationController.toScene(details.localPosition),
label: 'btn #$labelNumber'
);
chips.add(chipEntry);
labelNumber++;
});
}
}
class DraggableChip extends StatefulWidget {
const DraggableChip({
Key? key,
required this.chip,
this.onTap,
this.onDrag,
this.onDeleted,
}) : super(key: key);
final ChipEntry chip;
final VoidCallback? onTap;
final Function(Offset)? onDrag;
final VoidCallback? onDeleted;
#override
State<DraggableChip> createState() => _DraggableChipState();
}
class _DraggableChipState extends State<DraggableChip> with SingleTickerProviderStateMixin {
late final controller = AnimationController(vsync: this, duration: const Duration(milliseconds: 250));
bool drag = false;
Offset position = Offset.zero;
double scale = 0;
#override
void initState() {
super.initState();
controller.forward();
}
#override
void didUpdateWidget(covariant DraggableChip oldWidget) {
super.didUpdateWidget(oldWidget);
scale = controller.value = 1;
}
#override
Widget build(BuildContext context) {
final child = RawChip(
selected: drag,
showCheckmark: false,
selectedColor: Colors.teal,
backgroundColor: Colors.orange,
side: const BorderSide(width: 1, color: Colors.black12),
elevation: 4,
onDeleted: () async {
await controller.reverse();
widget.onDeleted?.call();
},
label: GestureDetector(
onLongPressStart: (d) => setState(() {
drag = true;
position = d.globalPosition;
}),
onLongPressMoveUpdate: (d) {
widget.onDrag?.call(d.globalPosition - position);
position = d.globalPosition;
},
onLongPressEnd: (d) => setState(() => drag = false),
child: InkWell(
onTap: widget.onTap,
child: Text(widget.chip.label),
),
),
);
return Positioned(
left: widget.chip.offset.dx,
top: widget.chip.offset.dy,
child: FractionalTranslation(
translation: const Offset(-0.5, -0.5),
child: ScaleTransition(
scale: controller,
child: child,
),
),
);
}
}

How to update child StateFulWidget value using parent stateful widget

I have two separate widgets. I want to update the child widget textFormField value when I click on the button in the parent widget.
I have provided the code below. How can I do this without getX or Provider in flutter? I looked for a solution to this problem but did not find a solution for this kind of problem.
Parent Widget
FutureBuilder(
future: SupervisorAttendanceServices.getAttendancesDetailsList(
widget.attendanceId),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.hasData) {
var data = snapshot.data['labour'];
return ListView.builder(
itemCount: data.length,
physics: const NeverScrollableScrollPhysics(),
shrinkWrap: true,
itemBuilder: (BuildContext context, int index) {
return LabourAttendanceWidget(
workerId: data[index]['worker_id'],
masterAttendanceId: widget.attendanceId,
name: data[index]['worker_name'],
wages: data[index]['attendance_worker_wages'],
isPrensent: data[index]
['attendance_worker_presense']
.toString());
});
} else if (snapshot.hasError) {
return const Center(
child: Text("Something went wrong !"),
);
} else {
return const Center(child: LinearProgressIndicator());
}
},
),
CHILD WIDGET
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_feather_icons/flutter_feather_icons.dart';
import 'package:get/get.dart';
import 'package:site_management/supervisors/screens/supervisor_attendance/controller/labour_attendance_controller.dart';
import 'package:site_management/supervisors/supervisor_services/supervisor_attendance_services.dart';
class LabourAttendanceWidget extends StatefulWidget {
const LabourAttendanceWidget({
Key? key,
required this.name,
required this.wages,
required this.isPrensent,
required this.workerId,
required this.masterAttendanceId,
}) : super(key: key);
final int workerId;
final int masterAttendanceId;
final String name;
final String wages;
final String isPrensent;
#override
State<LabourAttendanceWidget> createState() => _LabourAttendanceWidgetState();
}
class _LabourAttendanceWidgetState extends State<LabourAttendanceWidget> {
final TextEditingController _wagesController = TextEditingController();
String _character = "";
Timer? searchOnStoppedTyping;
LabourAttendanceController attendanceController =
Get.put(LabourAttendanceController());
_onChangeHandler(value) {
const duration = Duration(
milliseconds:
800); // set the duration that you want call search() after that.
if (searchOnStoppedTyping != null) {
setState(() => searchOnStoppedTyping?.cancel()); // clear timer
}
setState(() =>
searchOnStoppedTyping = Timer(duration, () => submitWages(value)));
}
submitWages(value) {
SupervisorAttendanceServices.storeWorkerWages(
widget.workerId, value, widget.masterAttendanceId);
}
#override
void initState() {
super.initState();
_character = widget.isPrensent;
_wagesController.text = widget.wages;
setState(() {});
}
#override
Widget build(BuildContext context) {
return Card(
child: Column(children: [
Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.start,
children: [
const SizedBox(
width: 10,
height: 50,
),
const Icon(FeatherIcons.user),
const SizedBox(
width: 20,
),
Text(
widget.name,
style: const TextStyle(fontSize: 18),
),
],
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
SizedBox(
width: 150,
height: 60,
child: TextFormField(
controller: _wagesController,
onChanged: _onChangeHandler,
decoration: const InputDecoration(
// border: OutlineInputBorder(),
hintText: "Wages",
prefixIcon: Icon(
Icons.wallet,
color: Colors.blue,
)),
)),
Row(
children: [
Radio(
value: "P",
groupValue: _character,
fillColor:
MaterialStateColor.resolveWith((states) => Colors.green),
onChanged: (selectedValue) {
setState(() {
_character = selectedValue.toString();
SupervisorAttendanceServices.changeAttendance(
widget.workerId,
_character,
widget.masterAttendanceId)
.then((response) {
if (response == 1) {
return null;
} else {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
behavior: SnackBarBehavior.floating,
content: Row(
children: const [
Icon(FeatherIcons.home),
SizedBox(
width: 10,
),
Text("Something went wrong !"),
],
),
),
// sb
);
}
});
});
attendanceController
.getAttendanceCount(widget.masterAttendanceId);
},
),
const Text("P"),
Radio(
value: "A",
fillColor:
MaterialStateColor.resolveWith((states) => Colors.red),
groupValue: _character,
onChanged: (selectedValue) {
setState(() {
_wagesController.text = "0";
_onChangeHandler("0");
_character = selectedValue.toString();
SupervisorAttendanceServices.changeAttendance(
widget.workerId,
_character,
widget.masterAttendanceId);
});
attendanceController
.getAttendanceCount(widget.masterAttendanceId);
}),
const Text("A"),
],
)
],
)
]),
);
}
}
First change your LabourAttendanceWidget to this:
class LabourAttendanceWidget extends StatefulWidget {
const LabourAttendanceWidget({
Key? key,
required this.name,
required this.wages,
required this.isPrensent,
required this.workerId,
required this.masterAttendanceId,
this.someString,
}) : super(key: key);
final int workerId;
final int masterAttendanceId;
final String name;
final String wages;
final String isPrensent;
final String someString;
#override
State<LabourAttendanceWidget> createState() => _LabourAttendanceWidgetState();
}
then in LabourAttendanceWidget's initState do this:
#override
void initState() {
super.initState();
_character = widget.isPrensent;
_wagesController.text = widget.someString ?? widget.wages;
setState(() {});
}
and in your parent widget first define this variable out of build method:
String? _value;
then do this:
return LabourAttendanceWidget(
workerId: data[index]['worker_id'],
masterAttendanceId: widget.attendanceId,
name: data[index]['worker_name'],
wages: data[index]['attendance_worker_wages'],
someString: _value,
isPrensent: data[index]
['attendance_worker_presense']
.toString());
then fill _value when came back from pop up and then call setstate.

Flutter widgets are not getting laid out correctly

The ListView widget which is getting generated as part of the CourseStream Stream Builder isn't getting laid out correctly, as shown in the picture below. I don't see any errors within the Debug Console.
Below is the code for the CourseStream which contains the ListView.
final coursesCollection = FirebaseFirestore.instance.collection('courses').limit(10).where('courseLive', isEqualTo: true);
class CourseStream extends StatelessWidget {
#override
Widget build(BuildContext context) {
return StreamBuilder<QuerySnapshot>(
stream: coursesCollection.snapshots(),
builder: (context, snapshot) {
if (!snapshot.hasData) {
if (snapshot.connectionState == ConnectionState.waiting) {
return Center(
child: CircularProgressIndicator(backgroundColor: kBrandColor),
);
}
}
final courseListStream = snapshot.data!.docs.map((course) {
return CourseData.fromDocument(course);
}).toList();
List<BadgedCourseCard> courseCards = [];
for (var course in courseListStream) {
final courseDocID = course.courseDocID;
final courseID = course.courseID;
final courseTitle = course.courseTitle;
final courseDescription = course.courseDescription;
final courseSubTitle = course.courseSubTitle;
final courseBadge = course.courseBadge;
final courseLevel = course.courseLevel;
final coursePaid = course.coursePaid;
final courseImage = course.courseImage;
final courseBgColor = hexToColor(course.courseBackgroundColor.toString());
final courseBgColor1 = hexToColor(course.courseBgColor1.toString());
final courseBgColor2 = hexToColor(course.courseBgColor2.toString());
final courseFgColor = hexToColor(course.courseFgColor.toString());
final courseDeliveryFormat = course.courseDeliveryFormat;
final courseLive = course.courseLive;
final badgedCourseCard = BadgedCourseCard(
courseTitle: courseTitle.toString(),
courseTitleTextColor: courseFgColor,
cardBackgroundColor: courseBgColor,
courseImage: courseImage.toString(),
courseCardTapped: () => print("Course Card Tapped"),
courseBookmarkTapped: () => print("Course Bookmark Tapped"),
);
return ListView(
physics: BouncingScrollPhysics(),
scrollDirection: Axis.horizontal,
children: courseCards,
);
},
);
}
}
Below is the code where the ListView is getting consumed.
class AppHome extends StatefulWidget {
#override
_AppHomeState createState() => _AppHomeState();
}
class _AppHomeState extends State<AppHome> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.transparent,
elevation: 0.0,
leading: Padding(
padding: EdgeInsets.only(left: 2.5.w),
child: IconButton(
onPressed: () => Navigator.of(context).push(ScaledAnimationPageRoute(AppDrawer())),
icon: Icon(
Icons.sort,
color: Theme.of(context).iconTheme.color,
size: 6.5.w,
),
),
),
actions: <Widget>[
Padding(
padding: EdgeInsets.only(right: 2.5.w),
child: IconButton(
onPressed: null,
icon: Icon(
Icons.search,
color: Theme.of(context).iconTheme.color,
size: 6.5.w,
),
),
),
],
),
body: Padding(
padding: EdgeInsets.symmetric(horizontal: 5.w),
child: Column(
children: [
CardHeader(
leadingText: "Courses",
trailingText: "View All",
),
SizedBox(height: 1.h),
Expanded(child: CourseStream()),
SizedBox(height: 2.h),
],
),
),
);
}
}
I'm not sure from where exactly the space is getting added below the Courses row. How can I fix it?
As discussed in the chat, this may be a solution for it.
Remove Expanded from Column() and wrap ListView with SizedBox so that you can limit height.
return SizedBox(
height: 20.5.h,
child: ListView(
physics: BouncingScrollPhysics(),
scrollDirection: Axis.horizontal,
children: courseCards,
),
);

Problem navigating to a route with Dialogflow and Flutter

I am working on an application with Flutter and Dialogflow and I want to make that when I say something specific to the bot, it will take me to a screen. That is, when writing 'depresion', I want it to take me to the context of the id to which 'depresion' corresponds. I have already created an Entitie in Dialogflow 'cual' with the id of the context I want to go to, but now I am not even shown the messages from the bot and it throws me this error.
UPDATE
This is the conversation that I usually have with the bot and it appears before placing the code that appears commented to use the parameters. The idea is that when that "3" appears, it takes me to the context screen that corresponds to that id "3" that I indicate in Navigator.pusnNamed
Flutter error
Dialogflow Entitie
class dialog_flow.dart
class FlutterFactsDialogFlow extends StatefulWidget {
FlutterFactsDialogFlow({Key key, this.title}) : super(key: key);
final String title;
#override
_FlutterFactsDialogFlowState createState() => new _FlutterFactsDialogFlowState();
}
class _FlutterFactsDialogFlowState extends State<FlutterFactsDialogFlow> {
final List<FactsMessage> _messages = <FactsMessage>[];
final TextEditingController _textController = new TextEditingController();
TtsProvider ttsProvider = TtsProvider();
Widget _queryInputWidget(BuildContext context) {
return Container(
child: Container(
margin: EdgeInsets.symmetric(horizontal: 8.0, vertical: 8.0),
child: Row(
children: <Widget>[
Flexible(
child: TextField(
controller: _textController,
onSubmitted: _submitQuery,
decoration: InputDecoration.collapsed(hintText: "Envia un mensaje"),
),
),
Container(
margin: EdgeInsets.symmetric(horizontal: 4.0),
child: IconButton(
icon: Icon(Icons.send),
onPressed: () => _submitQuery(_textController.text)),
),
],
),
),
);
}
void _dialogFlowResponse(context, query) async {
_textController.clear();
AuthGoogle authGoogle =
await AuthGoogle(fileJson: "assets/key.json").build();
Dialogflow dialogFlow =
Dialogflow(authGoogle: authGoogle, language: Language.spanish);
AIResponse response = await dialogFlow.detectIntent(query);
print(response.queryResult.parameters['cual']);
int id = int.parse(response.queryResult.parameters['cual']);
Navigator.pushNamed(context, 'home', arguments: id);
FactsMessage message = FactsMessage(
text: response.getMessage() ??
CardDialogflow(response.getListMessage()[0]).title,
name: "PsyBot",
type: false,
);
ttsProvider.hablar(response.getMessage());
setState(() {
_messages.insert(0, message);
});
}
void _submitQuery(String text) {
_textController.clear();
FactsMessage message = new FactsMessage(
text: text,
name: "TĂș",
type: true,
);
setState(() {
_messages.insert(0, message);
});
_dialogFlowResponse(context, text);
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
centerTitle: true,
title: Text("PsyBot"),
),
body: Column(children: <Widget>[
Flexible(
child: ListView.builder(
padding: EdgeInsets.all(8.0),
reverse: true, //Para mantener los Ășltimos mensajes al final
itemBuilder: (_, int index) => _messages[index],
itemCount: _messages.length,
)),
Divider(height: 1.0),
Container(
decoration: new BoxDecoration(color: Theme.of(context).cardColor),
child: _queryInputWidget(context),
),
]),
);
}
}
class fact_message.dart
class FactsMessage extends StatelessWidget {
FactsMessage({this.text, this.name, this.type});
final String text;
final String name;
final bool type;
List<Widget> botMessage(context) {
return <Widget>[
Container(
margin: const EdgeInsets.only(right: 16.0),
child: CircleAvatar(child: Text('Bot')),
),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(this.name,
style: TextStyle(fontWeight: FontWeight.bold)),
Container(
margin: const EdgeInsets.only(top: 5.0),
child: Text(text),
),
],
),
),
];
}
List<Widget> userMessage(context) {
return <Widget>[
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.end,
children: <Widget>[
Text(this.name, style: Theme.of(context).textTheme.subtitle1),
Container(
margin: const EdgeInsets.only(top: 5.0),
child: Text(text),
),
],
),
),
Container(
margin: const EdgeInsets.only(left: 16.0),
child: CircleAvatar(child: new Text(this.name[0])),
),
];
}
#override
Widget build(BuildContext context) {
return new Container(
margin: const EdgeInsets.symmetric(vertical: 10.0),
child: new Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: this.type ? userMessage(context) : botMessage(context),
),
);
}
}
class home_page.dart
class HomePage extends StatefulWidget {
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
PageController pageController = PageController(initialPage: 0);
int pageChanged = 0;
TtsProvider ttsProvider = TtsProvider();
#override
Widget build(BuildContext context) {
final preguntaP = Provider.of<PreguntaProvider>(context);
final int idTest = ModalRoute.of(context).settings.arguments;
return Scaffold(
appBar: AppBar(
title: Text('Home Page'),
actions: [
IconButton(icon: Icon(Icons.arrow_forward_ios), onPressed: (){
pageController.nextPage(duration: Duration(milliseconds: 150), curve: Curves.bounceInOut );
})
],
),
body: FutureBuilder<List<Pregunta>>(
future: preguntaP.fetchPreguntas(idTest),
builder: (context, AsyncSnapshot<List<Pregunta>> snapshot) {
if (snapshot.hasData){
// ttsprovider
//ttsProvider.hablar("Prueba");
List<Pregunta> preg = snapshot.data;
return PageView(
physics: new NeverScrollableScrollPhysics(),
pageSnapping: true,
reverse: false,
controller: pageController,
onPageChanged: (index){
setState(() {
pageChanged = index;
});
},
children: armarPreguntas(preg),
);
} else{
return Center(child: CircularProgressIndicator());
//return Container();
}
}
),
);
}
List<Widget> armarPreguntas(List<Pregunta> listaPreguntas){
final List<Widget> listadoWidget = [];
for (Pregunta item in listaPreguntas) {
listadoWidget.add(Pagina(item, pageController));
}
return listadoWidget;
}
}

how to call the method in provider inside the build method in flutter?

i have a method in the class which extends changeNotifier which gets the data from API. now I want to call this method whenever page is opened in the build method but when I call that method it is repeatedly called because of the notifyListeners method. I want to know how to call method only once.
ReportProvider.dart
class ReportProvider extends ChangeNotifier
{
static DateFormat dateFormat = new DateFormat('dd-MM-yyyy');
static DateFormat actualDateFormat = new DateFormat("yyyy-MM-dd");
String _toDate = dateFormat.format(new DateTime.now());
String _actualToDate = actualDateFormat.format(new DateTime.now());
String _actualFromDate = actualDateFormat.format(new DateTime.now().subtract(new Duration(days: 7)));
String _fromDate = dateFormat.format(new DateTime.now().subtract(new Duration(days: 7)));
bool _progressStatuc = false;
bool _chartVisible = true;
bool _errorVisible = false;
String _errorMessage;
String get errorMessage => _errorMessage;
bool get errorVisible => _errorVisible;
bool get chartVisible => _chartVisible;
bool get progressStatus => _progressStatuc;
String get toDate => _toDate;
String get fromDate => _fromDate;
List<PieData> _data = new List();
List<PieData> get data => _data;
Future<void> getReportData() async
{
Map<String,dynamic> sessiondata = await new Utilities().getSessionData();
int shopid = sessiondata['shopid'];
Map<String,String> reportData = new Map();
reportData['shopid'] = shopid.toString();
reportData["fromdate"] = _actualFromDate;
reportData["todate"] = _actualToDate;
String token = await new Utilities().getToken();
Map userHeader = {"token": token};
print(reportData.toString());
if(await new Utilities().checkInternet())
{
try
{
http.Response response = await http.post(EndPointUrl.report,body: reportData,headers: userHeader);
String message = json.decode(response.body)['message'];
List<ReportData> data = json.decode(response.body)['data'];
data.forEach((reportData){
_data.add(new PieData(reportData.menuname,reportData.itemcount));
});
notifyListeners();
}
catch(error)
{
_errorMessage = "Server error";
notifyListeners();
}
}
else
{
_progressStatuc = false;
_chartVisible = false;
_errorVisible = true;
_errorMessage = "No Internet Connection";
notifyListeners();
}
}
}
Report.dart
class Report extends StatefulWidget
{
#override
State<StatefulWidget> createState() {
return ReportState();
}
}
class ReportState extends State<Report>
{
#override
Widget build(BuildContext context) {
final reportProvider = Provider.of<ReportProvider>(context);
reportProvider.getReportData();
//yprint(reportProvider.data.toString());
if(reportProvider.errorMessage != null && reportProvider.errorMessage.contains("Internet"))
{
showDialog(
context: context,
builder: (BuildContext context){
return AlertDialog(
title: Text("Error"),
content: Text("${reportProvider.errorMessage}"),
actions: <Widget>[
FlatButton(
child: Text("ok"),
onPressed: (){
Navigator.pop(context);
},
)
],
);
});
}
return Stack(
children: <Widget>[
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Container(
margin: EdgeInsets.fromLTRB(8, MediaQuery.of(context).size.height*0.05,0, 0),
child: Text(
"Report",
style: TextStyle(fontSize: 28,color: Colors.black),
),
),
Row(
children: <Widget>[
Expanded(
flex: 3,
child: Container(
margin: EdgeInsets.fromLTRB(8, 8, 0, 0),
child: GestureDetector(
onTap: (){
reportProvider.selectDate(context, "fromdate");
},
child: Text(
"${reportProvider.fromDate}",
style: TextStyle(color: Colors.black,fontSize: 16),
),
),
),
),
Expanded(
flex: 1,
child: Text(
"To",
style: TextStyle(fontSize: 16,color: Colors.grey),
),
),
Expanded(
flex: 3,
child: GestureDetector(
onTap: (){
reportProvider.selectDate(context, "todate");
},
child: Text(
"${reportProvider.toDate}",
style: TextStyle(color: Colors.black,fontSize: 16),
),
),
),
Expanded(
flex: 1,
child: GestureDetector(
onTap: (){},
child: Icon(
Icons.check,
color: Theme.of(context).accentColor,
),
),
)
],
),
// Visibility(
// visible: reportProvider.chartVisible,
// child: charts.PieChart<PieData>(
// ),
// ),
Expanded(
child: Visibility(
visible: reportProvider.errorVisible,
child: Container(
alignment: Alignment.center,
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
SvgPicture.asset('assets/images/undraw_report.svg',width: MediaQuery.of(context).size.width,height: MediaQuery.of(context).size.height*0.40),
Text(
"No Reports are available",
style: TextStyle(color: Colors.black,fontSize: 20),
)
],
),
),
),
)
],
),
],
);
}
}
As per the documentation, build() method is going to be called every time something changes. If you want calls to be triggered just once, you could use initState() method and add some helpers methods to update the UI. An example can be found here: https://flutter.dev/docs/get-started/flutter-for/android-devs#what-is-the-equivalent-of-runonuithread-in-flutter
Example for async loading from the previous link, special attention to loadData method:
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
void main() {
runApp(SampleApp());
}
class SampleApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Sample App',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: SampleAppPage(),
);
}
}
class SampleAppPage extends StatefulWidget {
SampleAppPage({Key key}) : super(key: key);
#override
_SampleAppPageState createState() => _SampleAppPageState();
}
class _SampleAppPageState extends State<SampleAppPage> {
List widgets = [];
#override
void initState() {
super.initState();
loadData();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Sample App"),
),
body: ListView.builder(
itemCount: widgets.length,
itemBuilder: (BuildContext context, int position) {
return getRow(position);
},
),
);
}
Widget getRow(int i) {
return Padding(
padding: EdgeInsets.all(10.0),
child: Text("Row ${widgets[i]["title"]}"),
);
}
Future<void> loadData() async {
String dataURL = "https://jsonplaceholder.typicode.com/posts";
http.Response response = await http.get(dataURL);
setState(() {
widgets = json.decode(response.body);
});
}
}

Categories

Resources