im new to dart programming i want to implement a function to create dropdown buttons.
Contitons are:
-Dropdown button value should be taken as a variable.
Example function call:
buildDropdownField(dropdownHeader:"tank 1", dropdownValue:_tank2)
expected output of this function is to create a dropdown button and store its value into _tank2 so i can use it later on. So far ive tried different methods but i cant seem to get it right.
Heres my final function which does not display the value on screen but saves it tho the value given.
Widget buildDropdownField(
{required String dropdownHeader,
required String? dropdownValue,
required VoidCallback? OnChanged(Value)}) {
return Column(
children: <Widget>[
Text(dropdownHeader),
const SizedBox(
height: 10,
),
StatefulBuilder(
builder: (_, setDropState) {
return DropdownButton<String>(
value: dropdownValue,
icon: const Icon(Icons.arrow_downward),
elevation: 16,
style: const TextStyle(color: Colors.deepPurple),
underline: Container(
height: 2,
color: Colors.deepPurpleAccent,
),
onChanged: (value){
OnChanged(value);
},
items: <String>['-', 'Geçti', 'Kaldı', 'Belirsiz']
.map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
}).toList(),
);
},
)
],
);
}
And lastly heres the function call of my final function iteration:
buildDropdownField(dropdownHeader:"tank 1", dropdownValue:_tank2, OnChanged: (Value) {
setState(() {
tank1 = Value;
});
})
This is a custom expansion tile I used in a project of mine. It comes from the code from a package, slightly to my needs.
import 'package:flutter/material.dart';
const Duration _kExpand = Duration(milliseconds: 200);
class CustomExpansionTile extends StatefulWidget {
const CustomExpansionTile({
Key key,
this.leading,
#required this.title,
this.backgroundColor,
this.children = const <Widget>[],
this.trailing,
#required this.expandedItem,
#required this.expansionCallback,
#required this.onHeaderClick,
this.subtitleWidget,
}) : super(key: key);
/// A widget to display before the title.
///
/// Typically a [CircleAvatar] widget.
final Widget leading;
/// The primary content of the list item.
///
/// Typically a [Text] widget.
final Widget title;
final Widget subtitleWidget;
/// The widgets that are displayed when the tile expands.
///
/// Typically [ListTile] widgets.
final List<Widget> children;
/// The color to display behind the sublist when expanded.
final Color backgroundColor;
/// A widget to display instead of a rotating arrow icon.
final Widget trailing;
final ValueNotifier<Key> expandedItem;
final Function(bool hasExpanded) expansionCallback;
final Function() onHeaderClick;
#override
_CustomExpansionTileState createState() => _CustomExpansionTileState();
}
class _CustomExpansionTileState extends State<CustomExpansionTile>
with SingleTickerProviderStateMixin {
static final Animatable<double> _easeOutTween =
CurveTween(curve: Curves.easeOut);
static final Animatable<double> _easeInTween =
CurveTween(curve: Curves.easeIn);
static final Animatable<double> _halfTween =
Tween<double>(begin: 0.0, end: 0.5);
final ColorTween _borderColorTween = ColorTween();
final ColorTween _headerColorTween = ColorTween();
final ColorTween _iconColorTween = ColorTween();
final ColorTween _backgroundColorTween = ColorTween();
AnimationController _controller;
Animation<double> _iconTurns;
Animation<double> _heightFactor;
Animation<Color> _borderColor;
Animation<Color> _headerColor;
Animation<Color> _iconColor;
Animation<Color> _backgroundColor;
bool _isExpanded = false;
#override
void initState() {
super.initState();
_controller = AnimationController(duration: _kExpand, vsync: this);
_heightFactor = _controller.drive(_easeInTween);
_iconTurns = _controller.drive(_halfTween.chain(_easeInTween));
_borderColor = _controller.drive(_borderColorTween.chain(_easeOutTween));
_headerColor = _controller.drive(_headerColorTween.chain(_easeInTween));
_iconColor = _controller.drive(_iconColorTween.chain(_easeInTween));
_backgroundColor =
_controller.drive(_backgroundColorTween.chain(_easeOutTween));
_isExpanded = widget.expandedItem.value == widget.key;
if (_isExpanded) _controller.value = 1.0;
widget.expandedItem.addListener(listener);
}
void listener() {
setState(() {
_changeState(widget.expandedItem.value == widget.key);
});
}
#override
void dispose() {
_controller.dispose();
widget.expandedItem.removeListener(listener);
super.dispose();
}
void _changeState(bool isExpanded) {
setState(() {
_isExpanded = isExpanded;
if (_isExpanded) {
_controller.forward();
} else {
_controller.reverse().then<void>((void value) {
if (!mounted) return;
setState(() {
// Rebuild without widget.children.
});
});
}
PageStorage.of(context)?.writeState(context, _isExpanded);
});
}
//action to perform when the "expand" icon is pressed
void _onExpansionIconClick() {
widget.expansionCallback(!_isExpanded);
_changeState(!_isExpanded);
widget.expandedItem.value = _isExpanded ? widget.key : null;
}
Widget _buildChildren(BuildContext context, Widget child) {
final Color borderSideColor = _borderColor.value ?? Colors.transparent;
return Container(
decoration: BoxDecoration(
color: _backgroundColor.value ?? Colors.transparent,
border: Border(
top: BorderSide(color: borderSideColor),
bottom: BorderSide(color: borderSideColor),
),
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
ListTileTheme.merge(
iconColor: _iconColor.value,
textColor: _headerColor.value,
child: ListTile(
//if the list isExpanded, the click on the tile is disabled
//otherwise the action defined for the callback
//is performed
onTap: () => _isExpanded ? null : widget.onHeaderClick(),
leading: widget.leading,
title: widget.title,
subtitle: widget.subtitleWidget,
//arrow icon
trailing: InkWell(
onTap: _onExpansionIconClick,
child: Container(
padding: EdgeInsets.all(16),
//rotate arrow icon if the tile is expanded
child: widget.trailing ??
RotationTransition(
turns: _iconTurns,
child: const Icon(Icons.expand_more),
),
),
),
contentPadding: EdgeInsets.only(left: 16),
),
),
ClipRect(
child: Align(
heightFactor: _heightFactor.value,
child: child,
),
),
],
),
);
}
#override
void didChangeDependencies() {
final ThemeData theme = Theme.of(context);
_borderColorTween..end = theme.dividerColor;
_headerColorTween
..begin = theme.textTheme.subtitle1.color
..end = theme.accentColor;
_iconColorTween
..begin = theme.unselectedWidgetColor
..end = theme.accentColor;
_backgroundColorTween..end = widget.backgroundColor;
super.didChangeDependencies();
}
#override
Widget build(BuildContext context) {
final bool closed = !_isExpanded && _controller.isDismissed;
return AnimatedBuilder(
animation: _controller.view,
builder: _buildChildren,
child: closed ? null : Column(children: widget.children),
);
}
}
Related
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,
),
),
);
}
}
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.
I am trying to call a setState when a button is pressed so the ui can show the new list but even using functions i cant use setState or it will give me the error saying im calling setState inside a constructor.
This is my code for the statlessWidget:
class _MessageCard extends StatelessWidget {
final Mensagem message;
final int messageLenght;
final List<Mensagem> messageList;
var i;
_MessageCard(
{#required this.message,
#required this.messageLenght,
#required this.messageList});
#override
Widget build(BuildContext context) {
return Center(
child: Container(
width: 600,
child: InkWell(
child: Container(
width: 900,
color: Colors.grey[200],
child: Padding(
padding: const EdgeInsets.fromLTRB(12, 0, 12, 0),
child: Center(
child: Container(
width: 600,
child: Column(
children: <Widget>[
ListTile(
leading: CircleAvatar(
child: Icon(
Icons.notifications,
color: Colors.red[400],
),
backgroundColor: Colors.grey[200],
),
title: Text(
(this.message.vDescricao ?? '').trim(),
style: TextStyle(
fontSize: 14,
color: Colors.black,
),
),
subtitle: Text(
(this.message.vData ?? '').trim() +
' ' +
(this.message.vHora ?? '').trim(),
style: TextStyle(
color: Colors.red[400],
fontSize: 13,
),
),
trailing: FlatButton(
child: Text(
Translations.of(context)
.trans('finishmessageshort'),
),
onPressed: () => _showDeleteAlertMessage(
this.message.vNumero, context)),
),
Divider(
color: Colors.black54,
),
],
),
),
),
),
),
),
));
}
Future _showDeleteAlertMessage(String id, BuildContext context) {
return showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: new Text(
Translations.of(context).trans('finishmessage') + '?',
),
actions: <Widget>[
FlatButton(
child: new Text(
Translations.of(context).trans('closealert'),
),
onPressed: () {
Navigator.of(context).pop();
}),
FlatButton(
child: new Text(("Ok")),
onPressed: () =>
{_deleteMessage(id), Navigator.of(context).pop()},
)
],
);
});
}
_deleteMessage(String id) async {
for (i = 0; i < this.messageLenght; i++) {
if (this.messageList[0].vNumero == this.message.vNumero) {
this.messageList.removeAt(i);
_HomePageState().mensagemRepo.confirmMessage(this.message.vNumero);
await _HomePageState()._getMessages();
return this.messageList;
}
}
}
}
And this is my _getMessages()
_getMessages() async {
setState(() {
_loading = true;
_errorMsg = '';
});
try {
_messages = await mensagemRepo.getMessages();
print('loaded messages: ${_messages?.length}');
} catch (e) {
_errorMsg = e.toString();
}
setState(() {
_loading = false;
});
}
How can i make it so i can use this setState?
Thank you for your time and attention
Edit: Now updates List but not UI, because im not able to set HomePage state from MessageCard
You can only use setState in a StatefulWidget.
class MessageCard extends StatefulWidget {
#override
_MessageCardState createState() => _MessageCardState();
}
class _MessageCardState extends State<MessageCard> {
#override
Widget build(BuildContext context) {
// your build method here
}
}
Well, you can't set value for something that doesn't exist. Stateless by name itself makes it clear that it can't hold any state. Changing the widget to a stateful widget would work.
Stateless widget can not change the state once its rendered. To use setState and re-render the widget StatefulWidget is used.
Just change your MessageCard from Stateless Widget to StatefulWidget
class MessageCard extends StatefulWidget {
final Mensagem message;
final int messageLenght;
final List<Mensagem> messageList;
var i;
MessageCard(
{#required this.message,
#required this.messageLenght,
#required this.messageList});
#override
_MessageCardState createState() => _MessageCardState();
}
class _MessageCardState extends State<MessageCard> {
#override
Widget build(BuildContext context) {
// your build method here
}
}
Also, now "to use your MessageCard properties" like message, messageLenght, messageList, in _MessageCardState you have to use a property like widget.message, widget.messageList and widget.messageLenght respectively.
can this work to refresh the ui?
_getMessages() async {
_HomePageState()._messages = await mensagemRepo.getMessages();
print('loaded messages: ${_messages?.length}');
setState(() {
_HomePageState()._messagesList();
});
}
The code for _messagesList() is:
SliverChildBuilderDelegate _messagesList() {
int count() {
if (_errorMsg != '')
return 1;
else
return _messages == null ? 0 : _messages.length;
}
return SliverChildBuilderDelegate(
(BuildContext context, int index) {
print("i: $index");
if (_errorMsg != '') {
return Padding(
padding: EdgeInsets.all(20),
child: ErrorMessage(
error: _errorMsg,
),
);
} else {
return _MessageCard(
message: this._messages[index],
messageLength: this._messages.length,
messageList: this._messages);
}
},
childCount: count(),
);
}
I managed to make it work, by making both classes into one and calling a function to draw the messagecards, thank you all for your help and attention
I Am trying to have a horizontal ListView Widget that magnifies the center items. I tried using the normal ListView but I couldn't get the center items to magnify. Then while searching the flutter docs I came across ListWheelScrollView but unfortunately, it doesn't seem to support horizontal children layout. So basically am looking to create a horizontal ListView with center items magnification. I'd appreciate it if anyone can at least point me in the right direction. Thanks
Edit:
I have published package based on this.
pub.dev/packages/list_wheel_scroll_view_x
Here's my workaround.
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
class ListWheelScrollViewX extends StatelessWidget {
final Widget Function(BuildContext, int) builder;
final Axis scrollDirection;
final FixedExtentScrollController controller;
final double itemExtent;
final double diameterRatio;
final void Function(int) onSelectedItemChanged;
const ListWheelScrollViewX({
Key key,
#required this.builder,
#required this.itemExtent,
this.controller,
this.onSelectedItemChanged,
this.scrollDirection = Axis.vertical,
this.diameterRatio = 100000,
}) : super(key: key);
#override
Widget build(BuildContext context) {
return RotatedBox(
quarterTurns: scrollDirection == Axis.horizontal ? 3 : 0,
child: ListWheelScrollView.useDelegate(
onSelectedItemChanged: onSelectedItemChanged,
controller: controller,
itemExtent: itemExtent,
diameterRatio: diameterRatio,
physics: FixedExtentScrollPhysics(),
childDelegate: ListWheelChildBuilderDelegate(
builder: (context, index) {
return RotatedBox(
quarterTurns: scrollDirection == Axis.horizontal ? 1 : 0,
child: builder(context, index),
);
},
),
),
);
}
}
You can do this using the built-in ListWheelScrollView by pairing it with the Rotated Box. This is kind of a hack which works.
RotatedBox(
quarterTurns: -1,
child: ListWheelScrollView(
onSelectedItemChanged: (x) {
setState(() {
selected = x;
});
},
controller: _scrollController,
children: List.generate(
itemCount,
(x) => RotatedBox(
quarterTurns: 1,
child: AnimatedContainer(
duration: Duration(milliseconds: 400),
width: x == selected ? 60 : 50,
height: x == selected ? 60 : 50,
alignment: Alignment.center,
decoration: BoxDecoration(
color: x == selected ? Colors.red : Colors.grey,
shape: BoxShape.circle),
child: Text('$x')))),
itemExtent: itemWidth,
)),
);
Full source Code here
I recommend using this approach until this issue is resolved
Heres the output
copy this code and used it
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
class ListWheelScrollViewX extends StatelessWidget {
final Axis scrollDirection;
final List<Widget>? children;
final ScrollController? controller;
final ScrollPhysics? physics;
final double diameterRatio;
final double perspective;
final double offAxisFraction;
final bool useMagnifier;
final double magnification;
final double overAndUnderCenterOpacity;
final double itemExtent;
final double squeeze;
final ValueChanged<int>? onSelectedItemChanged;
final bool renderChildrenOutsideViewport;
final ListWheelChildDelegate? childDelegate;
final Clip clipBehavior;
const ListWheelScrollViewX({
Key? key,
this.scrollDirection = Axis.vertical,
this.controller,
this.physics,
this.diameterRatio = RenderListWheelViewport.defaultDiameterRatio,
this.perspective = RenderListWheelViewport.defaultPerspective,
this.offAxisFraction = 0.0,
this.useMagnifier = false,
this.magnification = 1.0,
this.overAndUnderCenterOpacity = 1.0,
required this.itemExtent,
this.squeeze = 1.0,
this.onSelectedItemChanged,
this.renderChildrenOutsideViewport = false,
this.clipBehavior = Clip.hardEdge,
required this.children,
}) : childDelegate = null,
super(key: key);
const ListWheelScrollViewX.useDelegate({
Key? key,
this.scrollDirection = Axis.vertical,
this.controller,
this.physics,
this.diameterRatio = RenderListWheelViewport.defaultDiameterRatio,
this.perspective = RenderListWheelViewport.defaultPerspective,
this.offAxisFraction = 0.0,
this.useMagnifier = false,
this.magnification = 1.0,
this.overAndUnderCenterOpacity = 1.0,
required this.itemExtent,
this.squeeze = 1.0,
this.onSelectedItemChanged,
this.renderChildrenOutsideViewport = false,
this.clipBehavior = Clip.hardEdge,
required this.childDelegate,
}) : children = null,
super(key: key);
#override
Widget build(BuildContext context) {
final _childDelegate = children != null
? ListWheelChildListDelegate(
children: children!.map((child) {
return RotatedBox(
quarterTurns: scrollDirection == Axis.horizontal ? 1 : 0,
child: child,
);
}).toList())
: ListWheelChildBuilderDelegate(
builder: (context, index) {
return RotatedBox(
quarterTurns: scrollDirection == Axis.horizontal ? 1 : 0,
child: childDelegate!.build(context, index),
);
},
);
return RotatedBox(
quarterTurns: scrollDirection == Axis.horizontal ? 3 : 0,
child: ListWheelScrollView.useDelegate(
controller: controller,
physics: FixedExtentScrollPhysics(),
diameterRatio: diameterRatio,
perspective: perspective,
offAxisFraction: offAxisFraction,
useMagnifier: useMagnifier,
magnification: magnification,
overAndUnderCenterOpacity: overAndUnderCenterOpacity,
itemExtent: itemExtent,
squeeze: squeeze,
onSelectedItemChanged: onSelectedItemChanged,
renderChildrenOutsideViewport: renderChildrenOutsideViewport,
clipBehavior: clipBehavior,
childDelegate: _childDelegate,
),
);
}
}
Example
ListWheelScrollViewX(
scrollDirection: Axis.horizontal,
itemExtent: 120,
children:...
),
Reference
list_wheel_scroll_view_x
changes:
convert to null safety
You can use this flutter package https://pub.dev/packages/carousel_slider.
It also has a very helpful description and few samples to see how it looks. And it's compatible with dart 2.0 too.
You can make this work with the help of ListView and PageView along with NotificationListener.
Below is my code for the same-
import 'dart:math';
import 'package:flutter/material.dart';
const SCALE_FRACTION = 0.9;
const FULL_SCALE = 1.0;
final PAGER_HEIGHT = SizeConfig.screenHeight*0.32;
const PAGER_WIDTH = double.infinity;
class PaymentWidget extends StatefulWidget {
#override
State<StatefulWidget> createState() => _PaymentState();
}
class _PaymentState extends State<PaymentWidget> {
double viewPortFraction = 0.9;
int currentPage = 1;
double page = 2.0;
PageController pageController;
final List<String> _cardsImages = ['image/path1', 'image/path2',
'image/path3', 'image/path4'];
#override
void initState() {
pageController = PageController(
initialPage: currentPage, viewportFraction: viewPortFraction);
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: null,
body: _creditCardsList()
);
}
Widget _creditCardsList() {
return ListView(
shrinkWrap: true,
children: <Widget>[
Container(
height: PAGER_HEIGHT,
child: NotificationListener<ScrollNotification>(
onNotification: (ScrollNotification notification) {
if (notification is ScrollUpdateNotification) {
setState(() {
page = pageController.page;
});
}
},
child: PageView.builder(
onPageChanged: (pos) {
setState(() {
currentPage = pos;
});
},
physics: BouncingScrollPhysics(),
controller: pageController,
itemCount: _cardsImages.length,
itemBuilder: (context, index) {
final scale =
max(SCALE_FRACTION, (FULL_SCALE - (index - page).abs()) + viewPortFraction);
return CreditCardTile(
_cardsImages[index], scale);
},
),
),
),
],
);
}
Widget CreditCardTile(String image, double scale) {
return Align(
alignment: Alignment.bottomCenter,
child:Container(
height: PAGER_HEIGHT * scale,
width: PAGER_WIDTH * scale,
child: Card(
elevation: 5,
shadowColor: constColors.blueWhiteShade,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10.0)
),
clipBehavior: Clip.antiAlias,
child: Image.asset(
image,
fit: BoxFit.cover,
),
)
) ,
);
}
Add 2 RotatedBox as Follows
StreamBuilder<DocumentSnapshot>(
stream: FirebaseFirestore.instance
.collection('Categories')
.doc('EcomCat')
.snapshots(),
builder: (BuildContext context,
AsyncSnapshot<DocumentSnapshot> snapshot) {
if (snapshot.data == null) {
return Center(
child: CircularProgressIndicator(),
);
}
final DocumentSnapshot document = snapshot.data!;
final Map<String, dynamic> documentData =
document.data() as Map<String, dynamic>;
final List Category = documentData['category'];
return RotatedBox(quarterTurns: 1,
child: ListWheelScrollView.useDelegate(magnification: 200,
itemExtent: 180,
childDelegate: ListWheelChildBuilderDelegate(
childCount: Category.length,
builder: (context,index){
return Row(
children: [
RotatedBox(quarterTurns: -1,
child: ElevatedButton(
style: ElevatedButton.styleFrom(
padding: EdgeInsets.all(10),
shadowColor: Colors.black,
primary: Colors.teal,
shape: RoundedRectangleBorder(
borderRadius:
BorderRadius.circular(18))),
onPressed: () {
setState(() {
categoryVal =
Category[index].toString();
});
},
child: Text(
Category[index].toString(),
style: themeData.textTheme.headline4,
),
),
),
],
);
},
)),
);
},
),
Transform.rotate(
angle: -math.pi / 2,
child: ListWheelScrollView()
}
use Transform.rotate
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';
}