I have a future builder witch i use to fetch data, i need to use what i get from the api to a text editing controller that has to be defined outside of build and i need to give the text editing controller an initial value, in this initial value i need to put data3.firstName and data3.lastName, for now the initial values are hardcoded.
Future<Response> fetchAccountData() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
String? authorization = prefs.getString('authorization');
var url = 'https://dev.api.wurk.skyver.co/api/v1/employees/account';
response1 = await http.get(
Uri.parse(url),
headers: <String, String>{
'authorization': authorization ?? basicAuth.toString()
},
);
return response1;
}
factory AccountData.fromJson(Map<String, dynamic> json) {
return AccountData(
firstName: json['firstName'],
lastName: json['lastName'],
phoneNumber: json['phoneNumber']);
}
}
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) {}
}
TextEditingController editedFirstName =
TextEditingController(text: 'firstName'); << needs to be data3.firstName
TextEditingController editedLastName =
TextEditingController(text: 'lastName');<< needs to be data3.lastName
return SafeArea(
child: WillPopScope(
onWillPop: () async {
Navigator.pushReplacement(
context,
MaterialPageRoute(
builder: (context) => const ProfileScreen(),
),
);
return shouldPop;
},
body: FutureBuilder<Response>(
future: futureData,
builder: (context, snapshot) {
if (snapshot.hasData) {
AccountData data3 = AccountData.fromJson( << data3 has data3.firstName, and
data3.lastName
json.decode(snapshot.data!.body),
);
CodePudding user response:
You can use late initialization like this:
late TextEditingController editedFirstName;
late TextEditingController editedLastName;
return SafeArea(
child: WillPopScope(
onWillPop: () async {
Navigator.pushReplacement(
context,
MaterialPageRoute(
builder: (context) => const ProfileScreen(),
),
);
return shouldPop;
},
body: FutureBuilder<Response>(
future: futureData,
builder: (context, snapshot) {
if (snapshot.hasData) {
AccountData data3 = AccountData.fromJson( << data3 has data3.firstName, and
data3.lastName
json.decode(snapshot.data!.body),
);
editedFirstName = TextEditingController(text: data3.firstname)
editedLastName = TextEditingController(text: data3.lastname)
CodePudding user response:
I don't fully understand the reason to have a global TextController (and I don't think its a good idea either) but using Riverpod it would look somethink like this:
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
class AccountData {
final String firstName;
final String lastName;
final String phoneNumber;
AccountData({
required this.firstName,
required this.lastName,
required this.phoneNumber,
});
factory AccountData.fromJson(Map<String, dynamic> json) {
return AccountData(
firstName: json['firstName'],
lastName: json['lastName'],
phoneNumber: json['phoneNumber'],
);
}
}
Future<Map<String, dynamic>> fetchAccountData() async {
final response = {
'firstName': 'Name',
'lastName': 'LastName',
'phoneNumber': '98786758690',
};
return Future.delayed(const Duration(seconds: 3), () => response);
}
final futureData = FutureProvider<AccountData>((_) async {
final data = await fetchAccountData();
return AccountData.fromJson(data);
});
final firstNameController = ChangeNotifierProvider<TextEditingController>((ref) {
final controller = TextEditingController();
ref.listen<AsyncValue<AccountData>>(futureData, (_, curr) {
curr.whenOrNull(
data: (d) => controller.text = d.firstName,
);
});
return controller;
});
final lastNameController = ChangeNotifierProvider<TextEditingController>((ref) {
final controller = TextEditingController();
ref.listen<AsyncValue<AccountData>>(futureData, (_, curr) {
curr.whenOrNull(
data: (d) => controller.text = d.lastName,
);
});
return controller;
});
void main() {
runApp(const ProviderScope(child: MyApp()));
}
class MyApp extends StatelessWidget {
const MyApp();
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: Scaffold(
body: Center(
child: MyWidget(),
),
),
);
}
}
class MyWidget extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final firstController = ref.watch(firstNameController.notifier);
final lastcontroller = ref.watch(lastNameController.notifier);
return ref.watch(futureData).when(
loading: () => const CircularProgressIndicator(),
error: (e, _) => TextButton(
child: Text('$e'),
onPressed: () => ref.refresh(futureData),
),
data: (_) {
return Column(
children: [
TextField(
controller: firstController,
),
TextField(
controller: lastcontroller,
),
]
);
}
);
}
}
Now following Hazar Belge using a late initializer is a good idea, without state managament the idea would be simply use a FutureBuilder and then pass down the data to an StatefulWidget which initialize the TextEditingControllers with that data, and again using didUpdateWidget, all of it without depending of an external package (the bigger your app you would need to use more packages to help you develop fast but I would recommend try with the basic to grasp all you can do with the framework)
