Reputation: 363
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,
)
],
),
],
),
);
}
}
Upvotes: 6
Views: 11945
Reputation: 679
my tricky is make one TextField
have properties autofocus: true,
with onChange(lock) => lock = true
then when initState is run or widget is build , we set data with if (lock == false)
setALLYourDataInController
code :
bool lock = false;
Widget build(BuildContext context) {
...
if (lock == false) {
firstNameController.text = detail?.firstName.toString() ?? '';
}
...
}
my TextField
TextField(
autofocus: true,
style: kTextFieldTextStyle,
decoration: kInputDecoration.copyWith(
hintText: 'Yogithesymbian',
prefixIcon: Icon(Icons.person_add),
suffixIcon: firstNameController.text.isNotEmpty
? IconButton(
onPressed: firstNameController.clear,
icon: Icon(
Icons.clear,
color: kPrimaryColorLight,
),
)
: null,
),
onChanged: (unuse) {
lock = true; // here is logic
// set.updatePounds(int.parse(pounds));
},
textInputAction: TextInputAction.next,
controller: firstNameController,
),
Upvotes: 0
Reputation: 752
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.
Upvotes: 4
Reputation: 1906
Use controller!
TextEditingController password = new TextEditingController();
TextFormField(
validator: (value) {
if (value.isEmpty) {
return "Could not be empty";
}
return null;
},
enabled: !isLoading,
controller: password,
obscureText: hidePassword,
decoration: InputDecoration(
suffix: IconButton(icon: Icon((hidePassword)?Icons.remove_red_eye:Icons.cancel),onPressed: () => setState(() => hidePassword = !hidePassword),),
labelText: "PASSWORD",
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(20))),
onFieldSubmitted: (String p) {
buttonPressed();
},
),
Then retrieve it using
var pass = username.text;
Obviously you could use an array of controller and then retrieve them
Upvotes: 3