I’m working with APIs and I want if the user is already logged in before when I open the app after the splash screen he gets redirected to the homepage directly instead of the agreement page (I want only the new logged-in users to see the agreement page).
Here’s main.dart:
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:project/firebase_options.dart';
import 'package:project/screens/home_page.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter/material.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:project/screens/splash_screen.dart';
import 'package:shared_preferences/shared_preferences.dart';
// Import your theme provider class
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform);
await dotenv.load();
}
class MyApp extends StatefulWidget {
const MyApp({Key? key,}) : super(key: key);
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
final FirebaseMessaging _firebaseMessaging = FirebaseMessaging.instance;
@override
void initState() {
super.initState();
_configureFirebaseMessaging();
}
Future<String?> _loadGlobalId() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
return prefs.getString('globalId');
}
@override
Widget build(BuildContext context) {
final Brightness brightness = MediaQuery.of(context).platformBrightness;
final bool isDarkMode = brightness == Brightness.dark;
return MaterialApp(
debugShowCheckedModeBanner: false,
theme: isDarkMode ? ThemeData.dark() : ThemeData.light(),
home: SplashScreen()
);
}
}
Here’s agreement_page.dart:
class AgreementPage extends StatefulWidget {
@override
_AgreementPageState createState() => _AgreementPageState();
}
class _AgreementPageState extends State<AgreementPage> {
bool _accepted = false;
bool _loggedIn = false;
String _code = '';
Color _buttonColor = Colors.grey;
String _url =
'https://example.com/auth/example';
@override
void initState() {
super.initState();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
automaticallyImplyLeading: false,
backgroundColor: Color(0xff429588),
title: Text('Terms and Conditions'),
),
body: Padding(
padding: const EdgeInsets.all(16.0),
child: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
'End-User License Agreement',
style: TextStyle(
fontSize: 24.0,
fontWeight: FontWeight.bold,
),
),
SizedBox(height: 16.0),
Text(
),
SizedBox(height: 16.0),
Row(
children: [
Checkbox(
value: _accepted,
onChanged: (value) {
setState(() {
_accepted = value!;
_buttonColor =
_accepted ? Color(0xff429588) : Colors.grey;
});
},
),
Text(
'I accept the terms and conditions',
style: TextStyle(fontSize: 16.0),
),
],
),
SizedBox(height: 16.0),
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
ElevatedButton(
onPressed: _accepted
? () async {
// Show a webview with the url from bottom to top
showModalBottomSheet(
context: context,
isScrollControlled: true,
builder: (context) => Container(
height:
MediaQuery.of(context).size.height * 0.9,
child: WebView(
initialUrl: _url,
javascriptMode: JavascriptMode.unrestricted,
navigationDelegate: (request) {
// Check if the user has logged in successfully
if (request.url.startsWith(
'https://exp.example.com/exp')) {
setState(() {
_loggedIn = true;
});
// Extract the code from the query parameter
final uri = Uri.parse(request.url);
final code = uri.queryParameters['code'];
if (code != null) {
setState(() {
_code = code;
});
}
// Close the webview and navigate to the homepage with the code
Navigator.pop(context);
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => HomePage(
code:
_code, // Pass the code to the homepage
),
),
);
return NavigationDecision.prevent;
}
return NavigationDecision.navigate;
},
gestureRecognizers: Set()
..add(
Factory<VerticalDragGestureRecognizer>(
() => VerticalDragGestureRecognizer(),
),
),
),
),
);
}
: null,
style: ButtonStyle(
backgroundColor:
MaterialStateProperty.all<Color>(_buttonColor),
), // Disable the button if not accepted
child: Text(
'Accept',
style: TextStyle(color: Colors.white),
),
),
ElevatedButton(
onPressed: () {
// Handle the case when the user doesn't accept the agreement.
// You can navigate back or perform any other action.
// Exit the app
exit(0);
},
style: ButtonStyle(
backgroundColor:
MaterialStateProperty.all<Color>(Color(0xff429588)),
),
child: Text(
'Don't Accept',
style: TextStyle(color: Colors.white),
),
),
],
),
],
),
),
),
);
}
}
Here’s home_page.dart:
class HomePage extends StatefulWidget {
const HomePage({Key? key, this.code}) : super(key: key);
final String? code;
@override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
late Future<Map<String, dynamic>> userInfo;
PersistentTabController _controller =
PersistentTabController(initialIndex: 0);
@override
void initState() {
super.initState();
if (widget.code != null && widget.code!.isNotEmpty) {
userInfo = _authenticate(widget.code!);
}
}
Future<Map<String, dynamic>> _authenticate(String code) async {
try {
final tokenData = await AuthService.authenticate(code);
final accessToken = tokenData['access_token'];
final refreshToken = tokenData['refresh_token'];
return await _fetchUserInfo(accessToken);
} catch (e) {
// Handle authentication error
print('Authentication error: $e');
throw e;
}
}
Future<Map<String, dynamic>> _fetchUserInfo(String accessToken) async {
try {
final userInfo = await UserService.fetchUserInfo(accessToken);
final String userName = userInfo['name'] ?? 'Unknown User';
final String userEmail = userInfo['email'] ?? 'Unknown Email';
final String userBirthdate = userInfo['birthdate'] ?? 'Unknown Birthdate';
final String userGender = userInfo['gender'] ?? 'Unknown Gender';
final String userRegion =
userInfo['address']['region'] ?? 'Unknown Region';
final String userPostalCode =
userInfo['address']['postal_code'] ?? 'Unknown Postal Code';
final String userCountry =
userInfo['address']['country'] ?? 'Unknown Country';
final String userPreferredUsername =
userInfo['preferred_username'] ?? 'Unknown Preferred Username';
final String sub = userInfo['sub'] ?? 'Unknown Global ID';
return {
'userName': userName,
'userEmail': userEmail,
'userBirthdate': userBirthdate,
'userGender': userGender,
'userRegion': userRegion,
'userPostalCode': userPostalCode,
'userCountry': userCountry,
'userPreferredUsername': userPreferredUsername,
'globalId': sub,
'_getTabAccessCode': await _getTabAccessCode(sub, accessToken),
};
} catch (e) {
// Handle user info fetch error
print('Error fetching user information: $e');
throw e;
}
}
Future<Map<String, dynamic>> _getTabAccessCode(
String globalId, String accessToken) async {
try {
final tabAccessCode =
await UserService.getTabAccessCode(globalId, accessToken);
print('Tab Access Code: $tabAccessCode');
print(
'Condition result: ${tabAccessCode['pid'] == tabAccessCode['doctorid']}');
print('PID: ${tabAccessCode['pid']}');
print('DoctorID: ${tabAccessCode['doctorid']}');
// Get a reference to the Firestore instance
FirebaseFirestore firestore = FirebaseFirestore.instance;
// Determine user type based on pid and doctorid
String userType = (tabAccessCode['pid'] == tabAccessCode['doctorid'])
? 'doctor'
: 'patient';
// Store the tab access code and user type in Firestore
await firestore.collection('users').doc(globalId).set({
'pid': tabAccessCode['pid'],
'TabAccCode': tabAccessCode['TabAccCode'],
'user_type': userType // Add this line
});
return {
'pid': tabAccessCode['pid'],
'TabAccCode': tabAccessCode['TabAccCode'],
'user_type': userType // Add this line
};
} catch (e) {
// Handle tab access code fetch error
print('Error getting Tab Access Code: $e');
throw e;
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
drawer: AppDrawer(userInfo: userInfo, controller: _controller),
body: PersistentTabView(
context,
controller: _controller,
screens: [
HomeContent(userInfo: userInfo),
FutureBuilder<Map<String, dynamic>>(
future: userInfo,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return Center(
child: SizedBox(
width: 50,
height: 50,
child: CircularProgressIndicator(),
),
);
} else if (snapshot.hasError) {
return Text('Error: ${snapshot.error}');
} else {
return ConversationPage(
TabAccCode: snapshot.data!['_getTabAccessCode']['TabAccCode'],
pid: snapshot.data!['_getTabAccessCode']['pid'],
user_type: snapshot.data!['_getTabAccessCode']['user_type'],
userInfo: userInfo,
);
}
},
),
AppointmentPage(),
Documents_Page(),
MyAppsPage()
],
items: [
PersistentBottomNavBarItem(
icon: Icon(Icons.home),
title: ("Home"),
activeColorPrimary: Color(0xff429588),
inactiveColorPrimary: Colors.grey,
),
PersistentBottomNavBarItem(
icon: Icon(Icons.message),
title: ("Messages"),
activeColorPrimary: Color(0xff429588),
inactiveColorPrimary: Colors.grey,
),
PersistentBottomNavBarItem(
icon: Icon(Icons.calendar_month),
title: ("Appointments"),
activeColorPrimary: Color(0xff429588),
inactiveColorPrimary: Colors.grey,
),
PersistentBottomNavBarItem(
icon: Icon(Icons.edit_document),
title: ("Documents"),
activeColorPrimary: Color(0xff429588),
inactiveColorPrimary: Colors.grey,
),
PersistentBottomNavBarItem(
icon: Icon(Icons.apps),
title: ("My Apps"),
activeColorPrimary: Color(0xff429588),
inactiveColorPrimary: Colors.grey,
),
],
),
);
}
}
I tried doing shared preferences like this:
splash_screen.dart
void initState() {
super.initState();
SystemChrome.setEnabledSystemUIMode(SystemUiMode.immersive);
// Introduce a 2-second delay before navigating to the agreement page
Future.delayed(Duration(seconds: 2), () async {
// Get the instance of SharedPreferences
SharedPreferences prefs = await SharedPreferences.getInstance();
// Check if the user is already logged in
bool isLoggedIn = prefs.getBool('isLoggedIn') ?? false;
// Use Navigator to navigate based on the login state
Navigator.of(context).pushReplacement(
MaterialPageRoute(
builder: (context) => isLoggedIn ? HomePage() : AgreementPage()),
);
});
}
and in the agreement_page.dart where there is the user accepts:
ElevatedButton(
onPressed: _accepted
? () async {
SharedPreferences prefs =
await SharedPreferences.getInstance();
prefs.setBool('isLoggedIn',
true); // Set to true when logging in
// Show a webview with the url from bottom to top
showModalBottomSheet(
context: context,
isScrollControlled: true,
builder: (context) => Container(
height:
MediaQuery.of(context).size.height * 0.9,
child: WebView(
initialUrl: _url,
javascriptMode: JavascriptMode.unrestricted,
navigationDelegate: (request) {
// Check if the user has logged in successfully
if (request.url.startsWith(
'https://example.com')) {
setState(() {
_loggedIn = true;
});
// Extract the code from the query parameter
final uri = Uri.parse(request.url);
final code = uri.queryParameters['code'];
if (code != null) {
setState(() {
_code = code;
});
}
// Close the webview and navigate to the homepage with the code
Navigator.pop(context);
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => HomePage(
code:
_code, // Pass the code to the homepage
),
),
);
return NavigationDecision.prevent;
}
return NavigationDecision.navigate;
},
gestureRecognizers: Set()
..add(
Factory<VerticalDragGestureRecognizer>(
() => VerticalDragGestureRecognizer(),
),
),
),
),
);
}
: null,
style: ButtonStyle(
backgroundColor:
MaterialStateProperty.all<Color>(_buttonColor),
), // Disable the button if not accepted
child: Text(
'Accept',
style: TextStyle(color: Colors.white),
),
),
but I got an error from home_page.dart:
"LateError (LateInitializationError: Field ‘userInfo’ has not been initialized.)"
If anyone could please help.
2
Answers
You are facing LateInitializationError in home_page.dart because when you are navigating to HomePage from AgreementPage you are passing the code, but when you navigate to HomePage from Splash you are not passing any code and hence your userInfo not get initialized because the condition fails.
In HomePage remove null operator from
String? code -> String code
to make the code field required, and not get stuck in the same problem again. Pass value of code in HomePage when navigating from SplashScreen.Hope you got the issue and this solution work for you.
I think you have 2 possibilities.
Inside the home_page.dart, make sure code != null by doing: