I have a small flutter application that uses Firebase Auth to login and then uses bindStream to query a list of documents from Firestore. It works from a fresh start/hot restart, but as soon as I logout I get a firebase/firestore permission error and subsequent login's don't refresh the stream. I thought that a GetxController disposes streams created via bindStream when the view that uses the controller is disposed. In this case, when I logout I pop off all routes via Get.offAll, but it appears the stream is still active and that's when the permissions error happens. But I'm not actually sure what is happening.
main.dart
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
Get.put(LoginController());
Get.put(AuthController(), permanent: true);
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return GetMaterialApp(
debugShowCheckedModeBanner: false,
title: 'GetX Firebase Firestore',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: SplashScreen(),
);
}
}
auth_controller.dart
class AuthController extends GetxController {
final AuthService _authService = AuthService();
AuthService get authService => _authService;
final LoginController _loginController = Get.find<LoginController>();
LoginController get loginController => _loginController;
Rxn<User> _user = Rxn<User>();
User? get user => _user.value;
@override
void onReady() async {
// bind auth state to _firebaesUser, but also give an initial value
_user = Rxn<User>(_authService.currentUser);
_user.bindStream(_authService.authState);
//run every time auth state changes
ever<User?>(_user, handleAuthChanged);
super.onReady();
}
handleAuthChanged(User? user) {
print("handleAuthChanged - ${user?.uid}");
if (user == null) {
Get.offAll(() => LoginScreen());
} else {
Get.offAll(() => HomeScreen(), binding: HomeBinding());
}
}
}
user_controller.dart
class UserController extends GetxController {
final UserRepository _userRepository = UserRepository();
final repository = UserRepository();
final users = Rx<List<FirestoreUser>>([]);
late Rx<FirestoreUser> _firestoreUser;
FirestoreUser get firestoreUser => _firestoreUser.value;
@override
void onInit() {
super.onInit();
final user = FirebaseAuth.instance.currentUser;
if (user == null) return;
_firestoreUser = Rx<FirestoreUser>(FirestoreUser.fromAuth(user));
// get user data from firestore
_firestoreUser.bindStream(_userRepository.getUserById(user.uid));
// query user collection
getAllUsers();
}
void getAllUsers() {
users.bindStream(repository.getAllUsers());
}
}
home_screen.dart
class HomeScreen extends GetView<UserController> {
const HomeScreen({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("All Sample Users"),
actions: [
IconButton(
onPressed: () => Get.to(ProfileScreen()),
icon: Icon(Icons.person),
),
],
),
body: Obx(
() => ListView.builder(
itemCount: controller.users.value.length,
itemBuilder: (context, index) {
final user = controller.users.value[index];
return ListTile(
leading: CircleAvatar(
backgroundImage: NetworkImage(user.photoURL),
),
title: Text(user.displayName),
);
},
),
),
);
}
}
home_binding.dart
class HomeBinding extends Bindings {
@override
void dependencies() {
Get.lazyPut<UserController>(() => UserController(), fenix: true);
}
}