I am using Flutter and firebase to create a web application. Below are the dependencies:
firebase_core: ^1.7.0
firebase_auth: ^3.1.3
cloud_firestore: ^2.5.3
Below are the script versions on web/index.html:
<script src="https://www.gstatic.com/firebasejs/8.6.1/firebase-app.js"></script>
<script src="https://www.gstatic.com/firebasejs/8.6.1/firebase-auth.js"></script>
<script src="https://www.gstatic.com/firebasejs/8.6.1/firebase-firestore.js"></script>
I am trying to navigate the user after login with the email and password method. The user should be navigate to admin page
or user page
depends on the user role
on cloud Firestore
. After login The user from login page it will navigate to the splash screen
where the user should be decided to navigate to next page depends on the user role. Its working up to splash screen but I am unable to do what I want todo, the pages are not to be navigated. Please let me know is this a correct way and Please give me a solution.
Below is my testing code on splash screen:
import 'dart:async';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import 'package:webfire/admin_screen.dart';
import 'package:webfire/home_screen.dart';
class SplashScreen extends StatefulWidget {
const SplashScreen({Key? key}) : super(key: key);
@override
_SplashScreenState createState() => _SplashScreenState();
}
class _SplashScreenState extends State<SplashScreen> {
String role = 'user';
@override
void initState() {
super.initState();
_checkRole();
}
final FirebaseAuth auth = FirebaseAuth.instance;
void _checkRole() async {
User? user = FirebaseAuth.instance.currentUser;
//User user = auth.currentUser;
final DocumentSnapshot snap = await FirebaseFirestore.instance
.collection('users')
.doc(user!.uid)
.get();
setState(() {
role = snap['role'];
});
if (role == 'user') {
navigateNext(const HomeScreen());
} else if (role == 'admin') {
navigateNext(const AdminScreen());
}
}
void navigateNext(Widget route) {
Timer(const Duration(milliseconds: 500), () {
Navigator.pushReplacement(
context, MaterialPageRoute(builder: (context) => route));
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: const [
Text('Welcome'),
],
),
));
}
}
Errors:
opening utils.dart and showing :return util.promiseToFuture(thenable);
Debug Console:
Launching lib\main.dart on Chrome in debug mode...
This app is linked to the debug service: ws://127.0.0.1:51669/T3RoGAblBBU=/ws
Debug service listening on ws://127.0.0.1:51669/T3RoGAblBBU=/ws
Running with sound null safety
Connecting to VM Service at ws://127.0.0.1:51669/T3RoGAblBBU=/ws
════════ Exception caught by services library ══════════════════════════════════
The following assertion was thrown during a platform message callback:
setState() callback argument returned a Future.
The setState() method on _LoginScreenState#5e295 was called with a closure or method that returned a Future. Maybe it is marked as "async".
Instead of performing asynchronous work inside a call to setState(), first execute the work (without updating the widget state), and then synchronously update the state inside a call to setState().
When the exception was thrown, this was the stack
C:/b/s/w/ir/cache/builder/src/out/host_debug/dart-sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/errors.dart 251:49 throw_
packages/flutter/src/widgets/framework.dart 1091:9 <fn>
packages/flutter/src/widgets/framework.dart 1106:14 setState
packages/webfire/login_screen.dart 150:33 <fn>
packages/flutter/src/material/ink_well.dart 989:21 [_handleTap]
packages/flutter/src/material/ink_well.dart 765:5 [_simulateTap]
packages/flutter/src/widgets/actions.dart 481:49 invoke
packages/flutter/src/widgets/actions.dart 520:20 invokeAction
packages/flutter/src/widgets/shortcuts.dart 830:36 handleKeypress
packages/flutter/src/widgets/shortcuts.dart 1171:20 [_handleOnKey]
packages/flutter/src/widgets/focus_manager.dart 1761:25 [_handleKeyMessage]
packages/flutter/src/services/hardware_keyboard.dart 830:17 handleRawKeyMessage
C:/b/s/w/ir/cache/builder/src/out/host_debug/dart-sdk/lib/_internal/js_dev_runtime/patch/async_patch.dart 84:54 runBody
C:/b/s/w/ir/cache/builder/src/out/host_debug/dart-sdk/lib/_internal/js_dev_runtime/patch/async_patch.dart 123:5 _async
packages/flutter/src/services/hardware_keyboard.dart 808:51 handleRawKeyMessage
packages/flutter/src/services/platform_channel.dart 73:49 <fn>
C:/b/s/w/ir/cache/builder/src/out/host_debug/dart-sdk/lib/_internal/js_dev_runtime/patch/async_patch.dart 84:54 runBody
C:/b/s/w/ir/cache/builder/src/out/host_debug/dart-sdk/lib/_internal/js_dev_runtime/patch/async_patch.dart 123:5 _async
packages/flutter/src/services/platform_channel.dart 72:58 <fn>
packages/flutter/src/services/binding.dart 379:35 <fn>
C:/b/s/w/ir/cache/builder/src/out/host_debug/dart-sdk/lib/_internal/js_dev_runtime/patch/async_patch.dart 84:54 runBody
C:/b/s/w/ir/cache/builder/src/out/host_debug/dart-sdk/lib/_internal/js_dev_runtime/patch/async_patch.dart 123:5 _async
packages/flutter/src/services/binding.dart 376:98 <fn>
C:/b/s/w/ir/cache/builder/src/out/host_debug/flutter_web_sdk/lib/_engine/engine/platform_dispatcher.dart 1034:13 invoke2
C:/b/s/w/ir/cache/builder/src/out/host_debug/flutter_web_sdk/lib/ui/src/ui/channel_buffers.dart 25:5 invoke
C:/b/s/w/ir/cache/builder/src/out/host_debug/flutter_web_sdk/lib/ui/src/ui/channel_buffers.dart 65:7 push
C:/b/s/w/ir/cache/builder/src/out/host_debug/flutter_web_sdk/lib/ui/src/ui/channel_buffers.dart 130:16 push
C:/b/s/w/ir/cache/builder/src/out/host_debug/flutter_web_sdk/lib/_engine/engine/platform_dispatcher.dart 302:25 invokeOnPlatformMessage
C:/b/s/w/ir/cache/builder/src/out/host_debug/flutter_web_sdk/lib/_engine/engine/keyboard.dart 130:39 [_handleHtmlEvent]
C:/b/s/w/ir/cache/builder/src/out/host_debug/flutter_web_sdk/lib/_engine/engine/keyboard.dart 39:7 <fn>
════════════════════════════════════════════════════════════════════════════════
════════ Exception caught by services library ══════════════════════════════════
Assertion failed:
!_pressedKeys.containsKey(event.physicalKey)
"A KeyDownEvent is dispatched, but the state shows that the physical key is already pressed. If this occurs in real application, please report this bug to Flutter. If this occurs in unit tests, please ensure that simulated events follow Flutter's event model as documented in `HardwareKeyboard`. This was the event: KeyDownEvent#af777(physicalKey: PhysicalKeyboardKey#70028(usbHidUsage: \"0x00070028\", debugName: \"Enter\"), logicalKey: LogicalKeyboardKey#b2c0d(keyId: \"0x10000000d\", keyLabel: \"Enter\", debugName: \"Enter\"), character: null, timeStamp: 0:00:28.610899)"
════════════════════════════════════════════════════════════════════════════════
Below is the Edited Code as per 1st Answer:
import 'dart:async';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import 'package:webfire/admin_screen.dart';
import 'package:webfire/home_screen.dart';
class SplashScreen extends StatefulWidget {
const SplashScreen({Key? key}) : super(key: key);
@override
_SplashScreenState createState() => _SplashScreenState();
}
class _SplashScreenState extends State<SplashScreen> {
String role = 'user';
@override
void initState() {
super.initState();
_checkRole();
}
final FirebaseAuth auth = FirebaseAuth.instance;
void _checkRole() async {
User? user = FirebaseAuth.instance.currentUser;
//User user = auth.currentUser;
final DocumentSnapshot snap = await FirebaseFirestore.instance
.collection('users')
.doc(user!.uid)
.get();
setState(() {
role = snap['role'];
});
}
void navigateNext(Widget route) {
Timer(const Duration(milliseconds: 500), () {
Navigator.pushReplacement(
context, MaterialPageRoute(builder: (context) => route));
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
if (role == 'user')
const HomeScreen(),
if (role == 'admin')
const AdminScreen(),
],
),
));
}
}
Debug console after edited:
Restarted application in 1,347ms.
════════ Exception caught by gesture ═══════════════════════════════════════════
The following assertion was thrown while handling a gesture:
setState() callback argument returned a Future.
The setState() method on _LoginScreenState#995aa was called with a closure or method that returned a Future. Maybe it is marked as "async".
Instead of performing asynchronous work inside a call to setState(), first execute the work (without updating the widget state), and then synchronously update the state inside a call to setState().
When the exception was thrown, this was the stack
C:/b/s/w/ir/cache/builder/src/out/host_debug/dart-sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/errors.dart 251:49 throw_
packages/flutter/src/widgets/framework.dart 1091:9 <fn>
packages/flutter/src/widgets/framework.dart 1106:14 setState
packages/webfire/login_screen.dart 150:33 <fn>
packages/flutter/src/material/ink_well.dart 989:21 [_handleTap]
packages/flutter/src/gestures/recognizer.dart 193:24 invokeCallback
packages/flutter/src/gestures/tap.dart 608:48 handleTapUp
packages/flutter/src/gestures/tap.dart 296:5 [_checkUp]
packages/flutter/src/gestures/tap.dart 230:7 handlePrimaryPointer
packages/flutter/src/gestures/recognizer.dart 558:9 handleEvent
packages/flutter/src/gestures/pointer_router.dart 94:12 [_dispatch]
packages/flutter/src/gestures/pointer_router.dart 139:9 <fn>
C:/b/s/w/ir/cache/builder/src/out/host_debug/dart-sdk/lib/_internal/js_dev_runtime/private/linked_hash_map.dart 21:13 forEach
packages/flutter/src/gestures/pointer_router.dart 137:17 [_dispatchEventToRoutes]
packages/flutter/src/gestures/pointer_router.dart 123:7 route
packages/flutter/src/gestures/binding.dart 440:19 handleEvent
packages/flutter/src/gestures/binding.dart 420:14 dispatchEvent
packages/flutter/src/rendering/binding.dart 278:11 dispatchEvent
packages/flutter/src/gestures/binding.dart 374:7 [_handlePointerEventImmediately]
packages/flutter/src/gestures/binding.dart 338:5 handlePointerEvent
packages/flutter/src/gestures/binding.dart 296:7 [_flushPointerEventQueue]
packages/flutter/src/gestures/binding.dart 279:32 [_handlePointerDataPacket]
C:/b/s/w/ir/cache/builder/src/out/host_debug/flutter_web_sdk/lib/_engine/engine/platform_dispatcher.dart 1018:13 invoke1
C:/b/s/w/ir/cache/builder/src/out/host_debug/flutter_web_sdk/lib/_engine/engine/platform_dispatcher.dart 182:5 invokeOnPointerDataPacket
C:/b/s/w/ir/cache/builder/src/out/host_debug/flutter_web_sdk/lib/_engine/engine/pointer_binding.dart 130:39 [_onPointerData]
C:/b/s/w/ir/cache/builder/src/out/host_debug/flutter_web_sdk/lib/_engine/engine/pointer_binding.dart 555:18 <fn>
C:/b/s/w/ir/cache/builder/src/out/host_debug/flutter_web_sdk/lib/_engine/engine/pointer_binding.dart 508:21 <fn>
C:/b/s/w/ir/cache/builder/src/out/host_debug/flutter_web_sdk/lib/_engine/engine/pointer_binding.dart 216:16 loggedHandler
Handler: "onTap"
Recognizer: TapGestureRecognizer#89932
debugOwner: GestureDetector
state: possible
won arena
finalPosition: Offset(677.0, 481.0)
finalLocalPosition: Offset(27.5, 27.8)
button: 1
sent tap down
════════════════════════════════════════════════════════════════════════════════
════════ Exception caught by rendering library ═════════════════════════════════
RenderCustomMultiChildLayoutBox object was given an infinite size during layout.
The relevant error-causing widget was
Scaffold
════════════════════════════════════════════════════════════════════════════════
════════ Exception caught by rendering library ═════════════════════════════════
_RenderInkFeatures object was given an infinite size during layout.
The relevant error-causing widget was
Scaffold
════════════════════════════════════════════════════════════════════════════════
════════ Exception caught by rendering library ═════════════════════════════════
RenderPhysicalModel object was given an infinite size during layout.
The relevant error-causing widget was
Scaffold
════════════════════════════════════════════════════════════════════════════════
════════ Exception caught by rendering library ═════════════════════════════════
A RenderFlex overflowed by Infinity pixels on the bottom.
The relevant error-causing widget was
Column
════════════════════════════════════════════════════════════════════════════════
════════ Exception caught by scheduler library ═════════════════════════════════
Cannot hit test a render box that has never been laid out.
════════════════════════════════════════════════════════════════════════════════
════════ Exception caught by scheduler library ═════════════════════════════════
Assertion failed:
!_debugDuringDeviceUpdate
is not true
════════════════════════════════════════════════════════════════════════════════
CodePudding user response:
You can't navigate (or return a Future) using Navigator
when an app is building, a better approach would be to show the relevant screen based on the role.
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: const [
if (role == 'user')
const HomeScreen(),
if (role == 'admin')
const AdminScreen(),
],
),
));
}
}
CodePudding user response:
To route the user to the correct page based on their role, you can perform the following two common steps:
- Create or update the database document associated with a user.
- Use custom claims in the
FireStore Authentication token
for each user.
Here are some videos that could help you to achieve your main goal:
https://www.youtube.com/watch?app=desktop&v=Tyk0Qte0moY - Flutter Role-based authorization with Firestore
https://www.youtube.com/watch?v=oFlHzF5U-HA - Setting up role based access control
Here is also additional documentation to review for the database rules:
Blog post: http://curlybrackets.co/blog/2016/03/07/implementing-roles-in-firebase/