i have two text editing controllers, im trying to give those text editing controllers initial values fetched from the api. I need to declare the text editing controllers globaly and give the initial value from the api, so what i need is to use data3.firstName and data3.lastName to the global variables, if i try declaring the variables and the function inside build it doesnt work, so just need to use the fetched data to global text editing controllers so i can give initial values.
late Future<Response> futureData;
@override
void initState() {
super.initState();
futureData = fetchAccountData();
}
final _formKey = GlobalKey<FormState>();
TextEditingController editedFirstName =
TextEditingController(text: 'Hello'); // needs to be data3.firsName
TextEditingController editedLastName = TextEditingController(text: 'Riverpod looks great');// needs to be data3.lastName
Future<void> putAccountData() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
String? authorization = prefs.getString('authorization');
var url = 'https://dev.api.wurk.skyver.co/api/v1/employees/account';
Map payload = {
"firstName": editedFirstName.text,
"lastName": editedLastName.text,
};
try {
final response = await http.put(Uri.parse(url),
headers: <String, String>{
'authorization': authorization ?? basicAuth.toString(),
"Content-Type": "application/json"
},
body: jsonEncode(payload));
} catch (er) {}
}
@override
Widget build(BuildContext context) {
var height = MediaQuery.of(context).size.height;
var width = MediaQuery.of(context).size.width;
return SafeArea(
child: WillPopScope(
onWillPop: () async {
Navigator.pushReplacement(
context,
MaterialPageRoute(
builder: (context) => const ProfileScreen(),
),
);
return shouldPop;
},
child: KeyboardDismisser(
gestures: const [
GestureType.onTap,
GestureType.onPanUpdateDownDirection
],
child: Form(
key: _formKey,
child: Scaffold(
resizeToAvoidBottomInset: false,
appBar: AppBar(
backgroundColor: Colors.blue,
title: const Text(
'Edit My Profile',
style: TextStyle(
color: Colors.white, fontWeight: FontWeight.bold),
),
leading: IconButton(
icon: const Icon(Icons.arrow_back),
onPressed: () {
Navigator.pushReplacement(
context,
MaterialPageRoute(
builder: (context) => const ProfileScreen(),
),
);
},
),
),
body: FutureBuilder<Response>(
future: futureData,
builder: (context, snapshot) {
if (snapshot.hasData) {
AccountData data3 = AccountData.fromJson(
json.decode(snapshot.data!.body),
);
return Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Container(
width: width,
height: height / 1.9,
decoration: BoxDecoration(
border: Border.all(
color: Colors.black,
width: 3,
),
),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Row(
children: [
const Padding(
padding: EdgeInsets.all(30),
child: Text(
"First Name:",
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold),
),
),
const Spacer(),
Padding(
padding: const EdgeInsets.all(30),
child: SizedBox(
width: width / 2.5,
child: Center(
child: TextFormField(
textAlignVertical:
TextAlignVertical.center,
//initialValue: editedFirstName.text = data3.firstName,
controller: editedFirstName,
// ..selection =
// TextSelection.collapsed(
// offset: data3
// .firstName.length),
decoration: InputDecoration(
contentPadding:
const EdgeInsets.symmetric(
vertical: 10.0,
horizontal: 10.0),
border: OutlineInputBorder(
borderRadius:
BorderRadius.circular(10),
borderSide: const BorderSide(
color: Colors.red,
width: 1),
),
),
style: const TextStyle(
fontSize: 17,
fontWeight: FontWeight.bold,
),
// inputFormatters: [
// LengthLimitingTextInputFormatter(15)
// ],
validator: (value) {
if (value == null ||
value.isEmpty) {
return 'Name is required';
}
return null;
},
),
),
),
),
],
),
Row(
children: [
const Padding(
padding: EdgeInsets.all(30),
child: Text(
'Last Name:',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
),
),
),
const Spacer(),
Padding(
padding: const EdgeInsets.all(30),
child: SizedBox(
width: width / 2.5,
child: TextFormField(
//initialValue: editedLastName.text = data3.lastName,
controller: editedLastName,
// ..selection =
// TextSelection.collapsed(
// offset:
// data3.lastName.length),
decoration: InputDecoration(
contentPadding:
const EdgeInsets.symmetric(
vertical: 10.0,
horizontal: 10.0),
border: OutlineInputBorder(
borderRadius:
BorderRadius.circular(10),
),
),
style: const TextStyle(
fontSize: 17,
fontWeight: FontWeight.bold),
),
),
),
],
),
CodePudding user response:
Looks like you want to provide an initial value to TextField, but the value is async fetched from the server.
You are on the right track to use a TextEditingController, but since you won't have the value ready at first, you should not use TextEditingController(text: 'Hello') when creating the controller.
Instead, you can create a controller without a default value, for example: final _controller = TextEditingController(). And then after you got the data, say after calling fetchAccountData() method in initState, you can assign the data to the controller using _controller.text = fetchedValue.
I see you are also using FutureBuilder. Depending on what you want to display before the "default values" are loaded, you might or might not need the FutureBuilder anymore.
Quick demo:
class QuickDemo extends StatefulWidget {
const QuickDemo({Key? key}) : super(key: key);
@override
_QuickDemoState createState() => _QuickDemoState();
}
class _QuickDemoState extends State<QuickDemo> {
final _controller = TextEditingController();
@override
void initState() {
super.initState();
_fetchData();
}
_fetchData() async {
// do your network call here
await Future.delayed(const Duration(seconds: 1));
// and then parse your response here
final data = 'Hello';
// eventually you will have data, so assign it:
_controller.text = data;
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: TextField(
controller: _controller,
),
),
);
}
}
