Last active
September 24, 2021 17:19
-
-
Save manikantag/18a0cab5ea2423e321aae62e50a3e00d to your computer and use it in GitHub Desktop.
Flutter Getx observable assignment in async issue
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import 'package:flutter/material.dart'; | |
import 'package:get/get.dart'; | |
void main() async { | |
WidgetsFlutterBinding.ensureInitialized(); | |
runApp(GetMaterialApp.router( | |
title: 'Test', | |
initialBinding: BindingsBuilder.put(() => AuthService(), permanent: true), | |
getPages: [ | |
GetPage( | |
name: '/', | |
page: () => const LoginScreen(), | |
binding: BindingsBuilder.put(() => LoginController()), | |
), | |
GetPage( | |
name: '/home', | |
page: () => const HomeScreen(), | |
binding: BindingsBuilder.put(() => HomeController()), | |
), | |
], | |
)); | |
} | |
// ----- models ----- | |
class AppUser { | |
final String uid; | |
final String mobile; | |
int count = 0; | |
AppUser({required this.uid, required this.mobile}); | |
AppUser.newUser(String uid, String mobile) : this(uid: uid, mobile: mobile); | |
AppUser.dummyUser() : this(uid: '', mobile: ''); | |
@override | |
String toString() { | |
return {'mobile': mobile, 'uid': uid, 'counter': count}.toString(); | |
} | |
} | |
class Teacher extends AppUser { | |
final String? regId; | |
Teacher({required String uid, required String mobile, this.regId}) | |
: super(uid: uid, mobile: mobile); | |
Teacher.newTeacher(String uid, String mobile) : this(mobile: mobile, uid: uid); | |
} | |
class Student extends AppUser { | |
final String? marks; | |
Student({required String uid, required String mobile, this.marks}) | |
: super(uid: uid, mobile: mobile); | |
Student.newStudent(String uid, String mobile) : this(uid: uid, mobile: mobile); | |
} | |
// ----- Auth service ----- | |
class AuthService extends GetxController { | |
Rx<bool> isAuthenticated = false.obs; | |
Rx<int> authCounter = 0.obs; | |
Rx<AppUser> currentUser = Rx(AppUser.dummyUser()); | |
/* @override | |
void onInit() { | |
currentUser = Student.newStudent('Student 1', 'Student 1 mobile').obs; // This is working | |
super.onInit(); | |
} */ | |
void doStudentLogin() async { | |
isAuthenticated.value = true; | |
// currentUser = Student.newStudent('Student 1', 'Student 1 mobile').obs; // This is working | |
currentUser = await Future.delayed( | |
const Duration(milliseconds: 10), | |
() => Student.newStudent('Student 1', 'Student 1 mobile').obs, // This is NOT working | |
); | |
print(currentUser); | |
} | |
void doTeacherLogin() async { | |
isAuthenticated.value = true; | |
// currentUser = Teacher.newTeacher('Teacher 1', 'Teacher 1 mobile').obs; // This is working | |
currentUser = await Future.delayed( | |
const Duration(milliseconds: 10), | |
() => Teacher.newTeacher('Teacher 1', 'Teacher 1 mobile').obs, // This is NOT working | |
); | |
print(currentUser); | |
} | |
void doLogout() { | |
isAuthenticated.value = false; | |
authCounter = 0.obs; | |
// currentUser = Teacher.newTeacher('Teacher 1', 'Teacher 1 mobile').obs; // This is working | |
Get.rootDelegate.offNamed('/'); | |
// Get.reset(); | |
// Get.reloadAll(force: true); | |
// Get.reload<AuthService>(); | |
// Get.deleteAll(force: true); | |
} | |
} | |
// ----- Login controller & page ----- | |
class LoginController extends GetxController { | |
final authService = Get.find<AuthService>(); | |
bool isLoggedin() => authService.isAuthenticated.value == true; | |
void doTeacherLogin() { | |
authService.doTeacherLogin(); | |
Get.rootDelegate.offNamed('/home'); | |
} | |
void doStudentLogin() { | |
authService.doStudentLogin(); | |
Get.rootDelegate.offNamed('/home'); | |
} | |
} | |
class LoginScreen extends GetView<LoginController> { | |
const LoginScreen({Key? key}) : super(key: key); | |
@override | |
Widget build(BuildContext context) { | |
return Scaffold( | |
body: Center( | |
child: Obx( | |
() => controller.isLoggedin() | |
? const Text('Already logged in -> Logout (state not cleared)') | |
: Column( | |
mainAxisAlignment: MainAxisAlignment.center, | |
children: [ | |
Text('authCounter: ${controller.authService.authCounter.value}'), | |
ElevatedButton( | |
onPressed: controller.doTeacherLogin, | |
child: const Text('Teacher Login'), | |
), | |
ElevatedButton( | |
onPressed: controller.doStudentLogin, | |
child: const Text('Student Login'), | |
), | |
], | |
), | |
), | |
), | |
); | |
} | |
} | |
// ----- Home controller & page ----- | |
class HomeController extends GetxController { | |
final AuthService _authService = Get.find<AuthService>(); | |
final Rx<int> homeCounter = 0.obs; | |
late final Rx<int> authCounter; | |
late final Rx<AppUser> currentUser; | |
@override | |
void onInit() { | |
authCounter = _authService.authCounter; | |
currentUser = _authService.currentUser; | |
super.onInit(); | |
} | |
void increment() { | |
homeCounter.value++; | |
authCounter.value++; | |
currentUser.value.count++; | |
} | |
void logout() => _authService.doLogout(); | |
} | |
class HomeScreen extends GetView<HomeController> { | |
const HomeScreen({Key? key}) : super(key: key); | |
@override | |
Widget build(BuildContext context) { | |
return Scaffold( | |
body: Obx( | |
() => Center( | |
child: Column( | |
mainAxisAlignment: MainAxisAlignment.center, | |
children: [ | |
Text('Auth counter: ${controller.authCounter.value}'), | |
Text('Home counter: ${controller.homeCounter.value}'), | |
Text( | |
'Current User: ${controller.currentUser.value is Teacher ? 'Teacher' : 'Student'}'), | |
Text('Current User: ${controller.currentUser.value}'), | |
ElevatedButton( | |
onPressed: controller.increment, | |
child: const Text('Increment counters'), | |
), | |
ElevatedButton( | |
onPressed: controller.logout, | |
child: const Text('Logout'), | |
), | |
], | |
), | |
), | |
), | |
); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import 'package:flutter/material.dart'; | |
import 'package:flutter/services.dart'; | |
import 'package:get/get.dart'; | |
void main() async { | |
WidgetsFlutterBinding.ensureInitialized(); | |
runApp(GetMaterialApp.router( | |
title: 'Test', | |
initialBinding: BindingsBuilder.put(() => _AuthService(), permanent: true), | |
getPages: [ | |
GetPage( | |
name: '/', | |
page: () => const _LoginScreen(), | |
binding: BindingsBuilder.put(() => _LoginController()), | |
), | |
GetPage( | |
name: '/teacher', | |
page: () => const _TeacherHomeScreen(), | |
binding: BindingsBuilder.put(() => _TeacherHomeController()), | |
), | |
GetPage( | |
name: '/student', | |
page: () => const _StudentHomeScreen(), | |
binding: BindingsBuilder.put(() => _StudentHomeController()), | |
), | |
], | |
)); | |
// Set overlay style status bar. It must run after MyApp(), because MaterialApp may override it. | |
SystemUiOverlayStyle systemUiOverlayStyle = const SystemUiOverlayStyle( | |
statusBarColor: Colors.transparent, systemNavigationBarColor: Colors.white); | |
SystemChrome.setSystemUIOverlayStyle(systemUiOverlayStyle); | |
} | |
// ----- Auth service ----- | |
class _AuthService extends GetxController { | |
// Rx<bool> isAuthenticated = false.obs; | |
Rx<_AppUser> currentUser = Rx(_AppUser.dummy()); | |
void doTeacherLogin() async { | |
// isAuthenticated.value = true; | |
currentUser = await Future.delayed( | |
const Duration(milliseconds: 200), | |
() => _Teacher.newTeacher('Teacher 1', 'Teacher 1 mobile').obs, | |
); | |
/* currentUser.value = await Future.delayed( | |
const Duration(milliseconds: 10), | |
() => Teacher.newTeacher('Teacher 1', 'Teacher 1 mobile'), | |
); */ | |
// ignore: avoid_print | |
print(currentUser); | |
Get.rootDelegate.offNamed('/teacher'); | |
} | |
void doStudentLogin() async { | |
// isAuthenticated.value = true; | |
currentUser = await Future.delayed( | |
const Duration(milliseconds: 200), | |
() => _Student.newStudent('Student 1', 'Student 1 mobile').obs, | |
); | |
/* currentUser.value = await Future.delayed( | |
const Duration(milliseconds: 10), | |
() => _Student.newStudent('Student 1', 'Student 1 mobile'), | |
); */ | |
// ignore: avoid_print | |
print(currentUser); | |
Get.rootDelegate.offNamed('/student'); | |
} | |
void doLogout() { | |
// isAuthenticated.value = false; | |
// currentUser(_AppUser.dummy()); | |
currentUser = _AppUser.dummy().obs; | |
// Get.reset(); | |
// Get.reloadAll(force: true); | |
// Get.reload<AuthService>(); | |
// Get.deleteAll(force: true); | |
Get.rootDelegate.offNamed('/'); | |
} | |
} | |
// ----- Login controller & page ----- | |
class _LoginController extends GetxController { | |
final authService = Get.find<_AuthService>(); | |
// bool isLoggedin() => authService.isAuthenticated.value; | |
} | |
class _LoginScreen extends GetView<_LoginController> { | |
const _LoginScreen({Key? key}) : super(key: key); | |
@override | |
Widget build(BuildContext context) { | |
return Scaffold( | |
body: Center( | |
child: Column( | |
mainAxisAlignment: MainAxisAlignment.center, | |
children: [ | |
// Obx(() => | |
// controller.isLoggedin() ? const Text('Logged in') : const Text('Not logged in')), | |
// Obx(() => Text('currentUser: ${controller.authService.currentUser.value}')), | |
ElevatedButton( | |
onPressed: controller.authService.doTeacherLogin, | |
child: const Text('Teacher Login'), | |
), | |
ElevatedButton( | |
onPressed: controller.authService.doStudentLogin, | |
child: const Text('Student Login'), | |
), | |
/* ElevatedButton( | |
onPressed: controller.authService.doLogout, | |
child: const Text('Logout'), | |
), */ | |
], | |
), | |
), | |
); | |
} | |
} | |
class _TeacherHomeController extends GetxController { | |
final authService = Get.find<_AuthService>(); | |
final Rx<_Teacher> currentUser = (Get.find<_AuthService>().currentUser.value as _Teacher).obs; | |
} | |
class _TeacherHomeScreen extends GetView<_TeacherHomeController> { | |
const _TeacherHomeScreen({Key? key}) : super(key: key); | |
@override | |
Widget build(BuildContext context) { | |
return Scaffold( | |
appBar: AppBar( | |
title: const Text('Welcom, Teacher'), | |
), | |
body: Container( | |
color: Colors.lightGreen.shade200, | |
child: Center( | |
child: Column( | |
mainAxisAlignment: MainAxisAlignment.center, | |
children: [ | |
Obx(() => Text('currentUser: ${controller.currentUser.value}')), | |
ElevatedButton( | |
onPressed: controller.authService.doLogout, | |
child: const Text('Logout'), | |
), | |
], | |
), | |
), | |
), | |
); | |
} | |
} | |
class _StudentHomeController extends GetxController { | |
final authService = Get.find<_AuthService>(); | |
final Rx<_Student> currentUser = (Get.find<_AuthService>().currentUser.value as _Student).obs; | |
} | |
class _StudentHomeScreen extends GetView<_StudentHomeController> { | |
const _StudentHomeScreen({Key? key}) : super(key: key); | |
@override | |
Widget build(BuildContext context) { | |
return Scaffold( | |
appBar: AppBar( | |
title: const Text('Welcom, Student'), | |
), | |
body: Container( | |
color: Colors.lightBlue.shade200, | |
child: Center( | |
child: Column( | |
mainAxisAlignment: MainAxisAlignment.center, | |
children: [ | |
Obx(() => Text('currentUser: ${controller.currentUser.value}')), | |
ElevatedButton( | |
onPressed: controller.authService.doLogout, | |
child: const Text('Logout'), | |
), | |
], | |
), | |
), | |
), | |
); | |
} | |
} | |
// ----- models ----- | |
class _AppUser { | |
final String uid; | |
final String mobile; | |
int count = 0; | |
_AppUser({required this.uid, required this.mobile}); | |
_AppUser.dummy() : this(uid: 'dummy', mobile: ''); | |
@override | |
String toString() { | |
return {'mobile': mobile, 'uid': uid, 'counter': count}.toString(); | |
} | |
} | |
class _Teacher extends _AppUser { | |
final String? regId; | |
_Teacher({required String uid, required String mobile, this.regId}) | |
: super(uid: uid, mobile: mobile); | |
_Teacher.newTeacher(String uid, String mobile) : this(mobile: mobile, uid: uid); | |
} | |
class _Student extends _AppUser { | |
final String? marks; | |
_Student({required String uid, required String mobile, this.marks}) | |
: super(uid: uid, mobile: mobile); | |
_Student.newStudent(String uid, String mobile) : this(uid: uid, mobile: mobile); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Fix is to add
await
at line 112 & 117.