start_workout.dart
import 'package:flutter/material.dart';
import 'package:fultter_ultralifestyle/src/models/models.dart' show SetModel;
import 'package:fultter_ultralifestyle/src/presentation/widgets/dynamic_widget.dart';
class WorkoutStartScreen extends StatefulWidget {
static const String routeName = "workoutStart";
@override
_WorkoutStartScreenState createState() => _WorkoutStartScreenState();
}
class _WorkoutStartScreenState extends State<WorkoutStartScreen> {
final List<SetModel> sets = [
];
void _addSet() {
final id = sets.length;
sets.add(SetModel(id: id, pounds: 0, reps: 0));
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Workout tracker"),
centerTitle: true,
),
body: Container(
child: Column(
children: <Widget>[
Expanded(
child: ListView.builder(
itemCount: sets.length,
itemBuilder: (BuildContext context, int index) {
return DynamicWidget(
set: sets[index],
pos: index +1,
delete: () {
sets.removeAt(index);
setState(() {});
},
);
}),
)
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: () => _addSet(),
child: Icon(Icons.add),
),
);
}
}
dynamic_widget.dart
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:fultter_ultralifestyle/src/models/models.dart';
import 'package:fultter_ultralifestyle/src/presentation/widgets/text_widget.dart';
class DynamicWidget extends StatelessWidget {
final TextEditingController poundsController = TextEditingController();
final TextEditingController repsController = TextEditingController();
final SetModel set;
final int pos;
Function delete;
DynamicWidget({
Key key,
@required this.set,
@required this.pos,
@required this.delete,
}) : super(key: key);
@override
Widget build(BuildContext context) {
final Size size = MediaQuery.of(context).size;
return Container(
child: Column(
children: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Container(
child: text(
caption: "SET $pos",
),
),
SizedBox(width: 20.0),
Container(
width: size.width / 4,
child: Column(
children: <Widget>[
TextField(
controller: poundsController,
keyboardType: TextInputType.number,
inputFormatters: [
WhitelistingTextInputFormatter.digitsOnly,
],
),
SizedBox(height: 10),
text(caption: "pounds".toUpperCase()),
],
),
),
SizedBox(
width: 20,
),
text(caption: "X"),
SizedBox(
width: 20,
),
Container(
width: size.width / 4,
child: Column(
children: <Widget>[
TextField(
controller: repsController,
keyboardType: TextInputType.number,
inputFormatters: [
WhitelistingTextInputFormatter.digitsOnly
],
),
SizedBox(height: 10),
text(caption: "reps".toUpperCase()),
],
),
),
SizedBox(width: 5.0),
IconButton(
icon: Icon(Icons.delete_forever),
onPressed: delete,
)
],
),
],
),
);
}
}
I solved this just by making your SetModel
update when the text changes in your text field. The main parts are the updatePounds
and updateReps
methods as well as the onChanged
methods in the text fields in the dynamic widget.
I also made sure to initialize the controllers with the values contained in the SetModel
when the dynamic widget is built.
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
void main() {
runApp(MaterialApp(
home: WorkoutStartScreen(),
));
}
class SetModel {
final int id;
int pounds;
int reps;
SetModel({
this.id,
this.pounds,
this.reps,
});
void updatePounds(int pounds) {
this.pounds = pounds;
}
void updateReps(int reps) {
this.reps = reps;
}
}
class WorkoutStartScreen extends StatefulWidget {
static const String routeName = "workoutStart";
@override
_WorkoutStartScreenState createState() => _WorkoutStartScreenState();
}
class _WorkoutStartScreenState extends State<WorkoutStartScreen> {
final List<SetModel> sets = [];
void _addSet() {
final id = sets.length;
setState(() {
sets.add(SetModel(id: id, pounds: 0, reps: 0));
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Workout tracker"),
centerTitle: true,
),
body: Container(
child: Column(
children: <Widget>[
Expanded(
child: ListView.builder(
itemCount: sets.length,
itemBuilder: (BuildContext context, int index) {
return DynamicWidget(
set: sets[index],
pos: index + 1,
delete: () {
setState(() {
sets.removeAt(index);
});
},
);
},
),
)
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: () => _addSet(),
child: Icon(Icons.add),
),
);
}
}
class DynamicWidget extends StatelessWidget {
final TextEditingController poundsController = TextEditingController();
final TextEditingController repsController = TextEditingController();
final SetModel set;
final int pos;
final Function delete;
DynamicWidget({
Key key,
@required this.set,
@required this.pos,
@required this.delete,
}) : super(key: key);
@override
Widget build(BuildContext context) {
final Size size = MediaQuery.of(context).size;
if (set.pounds != 0) {
poundsController.text = set.pounds.toString();
}
if (set.reps != 0) {
repsController.text = set.reps.toString();
}
return Container(
child: Column(
children: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Container(
child: Text(
"SET $pos",
),
),
SizedBox(width: 20.0),
Container(
width: size.width / 4,
child: Column(
children: <Widget>[
TextField(
controller: poundsController,
onChanged: (String pounds) {
set.updatePounds(int.parse(pounds));
},
keyboardType: TextInputType.number,
inputFormatters: [WhitelistingTextInputFormatter.digitsOnly],
),
SizedBox(height: 10),
Text("pounds".toUpperCase()),
],
),
),
SizedBox(
width: 20,
),
Text("X"),
SizedBox(
width: 20,
),
Container(
width: size.width / 4,
child: Column(
children: <Widget>[
TextField(
controller: repsController,
onChanged: (String reps) {
set.updateReps(int.parse(reps));
},
keyboardType: TextInputType.number,
inputFormatters: [WhitelistingTextInputFormatter.digitsOnly],
),
SizedBox(height: 10),
Text("reps".toUpperCase()),
],
),
),
SizedBox(width: 5.0),
IconButton(
icon: Icon(Icons.delete_forever),
onPressed: delete,
)
],
),
],
),
);
}
}
Basically, the dynamic widgets are being redrawn every time you call setState
from its parent. Since you never told the dynamic widgets to save anything, they are redrawn from scratch. This is still happening, except now we give them some initial information.
Here is a video of it working.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With