我对在 flutter、bloc 上开发应用程序非常陌生。我有一个问题,我试图检索alert_my.dart 页面的
userId
,但我无法获取userId
。我正在使用 API,因此使用令牌。我解码并获取 id 并将其保存为 userId
,但我无法在alert_my.dart 页面上获取 userId
。我希望你能帮助我。
//login_cubit.dart
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:dartz/dartz.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:jwt_decoder/jwt_decoder.dart';
import '../../domain/use cases/login_user_usecase.dart';
import '../../domain/entities/user.dart';
import '../../../../../core/errors/failure.dart';
import 'package:equatable/equatable.dart';
part 'login_state.dart';
class LoginCubit extends Cubit<LoginState> {
final LoginUserUseCase loginUserUseCase;
LoginCubit({required this.loginUserUseCase}) : super(LoginInitial());
Future<void> loginUser({
required String email,
required String password,
}) async {
emit(LoginLoading());
try {
final Either<Failure, User?> result = await loginUserUseCase(
email: email,
password: password,
);
result.fold(
(failure) {
emit(LoginError(message: _mapFailureToMessage(failure)));
},
(user) async {
if (user != null && user.token != null) {
// Decode the JWT token to extract the user ID
String userId = _extractUserIdFromToken(user.token!);
// Save the user token and userId to SharedPreferences
await _saveUserCredentials(user.token!, userId);
emit(LoginLoaded(user: user));
} else {
emit(LoginError(message: 'Failed to retrieve user.'));
}
},
);
} catch (e) {
emit(LoginError(message: e.toString()));
}
}
// Function to decode the JWT token and extract the user ID
String _extractUserIdFromToken(String token) {
Map<String, dynamic> decodedToken = JwtDecoder.decode(token);
print("Decoded token: $decodedToken"); // Debug print
return decodedToken['id']; // Extract the 'id' from the decoded token
}
// Save token and userId to SharedPreferences
Future<void> _saveUserCredentials(String token, String userId) async {
final prefs = await SharedPreferences.getInstance();
// Remove previous token and user ID if they exist
await prefs.remove('user_token');
await prefs.remove('user_id');
// Save new credentials
await prefs.setString('user_token', token);
await prefs.setString('user_id', userId);
// Verify successful save
print("User ID saved: $userId");
print("Token saved: $token");
}
// Retrieve userId from SharedPreferences
Future<String?> getUserId() async {
final prefs = await SharedPreferences.getInstance();
String? userId = prefs.getString('user_id');
if (userId == null) {
print("No User ID found in SharedPreferences");
} else {
print("User ID retrieved: $userId");
}
return userId;
}
// Map the failure to a user-friendly error message
String _mapFailureToMessage(Failure failure) {
if (failure is ServerFailure) {
return 'Server error occurred. Please try again later.';
} else if (failure is NetworkFailure) {
return 'Please check your internet connection.';
} else {
return 'Unexpected error occurred. Please try again.';
}
}
}
//login_screen.dart
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:shared_preferences/shared_preferences.dart';
import '../../presentation/bloc/login_cubit.dart';
class LoginScreen extends StatefulWidget {
const LoginScreen({Key? key}) : super(key: key);
@override
_LoginScreenState createState() => _LoginScreenState();
}
class _LoginScreenState extends State<LoginScreen> {
final _formKey = GlobalKey<FormState>();
final Map<String, String> _loginData = {};
@override
Widget build(BuildContext context) {
return Scaffold(
body: BlocListener<LoginCubit, LoginState>(
listener: (context, state) async {
if (state is LoginLoaded) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Login successful!')),
);
// Save user details to SharedPreferences
final prefs = await SharedPreferences.getInstance();
await prefs.setString('user_token', state.user.token ?? '');
await prefs.setString(
'user_id', state.user.userId); // Save only necessary details
// Navigate to AlertMyPage and pass the user object
Navigator.pushReplacementNamed(
context,
'/alert_my',
arguments: state.user, // Pass the User object to AlertMyPage
);
} else if (state is LoginError) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(state.message)),
);
}
},
child: Center(
child: SingleChildScrollView(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Column(
children: [
Image.asset(
'assets/KMS.png',
height: 300, // Adjust the height of the logo here
),
Text(
'Login to Continue',
style: TextStyle(
fontWeight: FontWeight.w700,
fontSize: 14,
color: Color(0xFF828282),
),
),
],
),
const SizedBox(height: 20),
Padding(
padding: const EdgeInsets.all(20.0),
child: Form(
key: _formKey,
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
TextFormField(
decoration: InputDecoration(
labelText: 'Email',
filled: true,
fillColor: Color(0xFFF3F8FF),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
),
),
onSaved: (value) => _loginData['email'] = value!,
validator: (value) {
if (value == null || value.isEmpty) {
return 'Please enter your email';
}
return null;
},
),
const SizedBox(height: 20),
TextFormField(
obscureText: true,
decoration: InputDecoration(
labelText: 'Password',
filled: true,
fillColor: Color(0xFFF3F8FF),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
),
),
onSaved: (value) => _loginData['password'] = value!,
validator: (value) {
if (value == null || value.isEmpty) {
return 'Please enter your password';
}
return null;
},
),
],
),
),
),
Container(
padding: EdgeInsets.only(left: 200),
child: TextButton(
onPressed: () {
// Add forgot password functionality if needed
},
child: Text(
'FORGOT PASSWORD?',
style: TextStyle(
color: Color(0xFF3E4044),
fontWeight: FontWeight.w500,
fontSize: 11,
decoration: TextDecoration.underline,
),
),
),
),
const SizedBox(height: 1),
ElevatedButton(
onPressed: () {
if (_formKey.currentState!.validate()) {
_formKey.currentState!.save();
// Trigger login action
context.read<LoginCubit>().loginUser(
email: _loginData['email']!,
password: _loginData['password']!,
);
}
},
style: ElevatedButton.styleFrom(
backgroundColor: Color(0xFF2B479A), // Adjust button color
fixedSize: const Size(315, 45), // Button size
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.zero,
),
),
child: const Text(
'LOGIN',
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 16,
color: Colors.white, // Text color
),
),
),
const SizedBox(height: 10),
Container(
child: TextButton(
onPressed: () {
Navigator.pushNamed(context, '/register_screen');
},
child: const Text(
'Don\'t have an account? Register',
style: TextStyle(
color: Color(0xFF3E4044),
fontWeight: FontWeight.w500,
fontSize: 12,
),
),
),
),
const SizedBox(height: 20),
],
),
),
),
),
);
}
}
//alert_my.dart
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import '../../../authentication/register/domain/entities/user.dart';
import '../../../authentication/register/presentation/bloc/login_cubit.dart';
class AlertMyPage extends StatelessWidget {
const AlertMyPage({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("Alert Page"),
),
body: FutureBuilder<String?>(
future: _getUserIdAfterDelay(context),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const Center(child: CircularProgressIndicator());
}
if (snapshot.hasError || snapshot.data == null) {
return const Center(child: Text('Error: User not found'));
}
final String userId = snapshot.data!;
final User user = ModalRoute.of(context)!.settings.arguments as User;
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 27.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Row(
children: [
ClipOval(
child: Image.asset(
'assets/profile_picture.png',
fit: BoxFit.cover,
width: 60,
height: 60,
),
),
const SizedBox(width: 20),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'Welcome back,',
style: TextStyle(fontSize: 15),
),
Text(
user.fullName,
style: const TextStyle(
fontSize: 20, fontWeight: FontWeight.w600),
),
const SizedBox(height: 5),
Text(
'User ID: $userId',
style:
TextStyle(fontSize: 16, color: Colors.grey[600]),
),
],
),
],
),
const SizedBox(height: 25),
const Padding(
padding: EdgeInsets.only(top: 6.0, left: 3.0),
child: Text(
'My Aqua Farms',
style: TextStyle(fontSize: 19, fontWeight: FontWeight.w700),
),
),
const SizedBox(height: 20),
Expanded(
child: ListView(
children: [
FarmCard(
farmName: 'Felda Bukit Ramun',
pondNumber: 1,
alert: 'Voltage Drop',
),
const SizedBox(height: 16),
FarmCard(
farmName: 'Felda Bukit Ramun',
pondNumber: 2,
alert: 'Voltage Drop',
),
const SizedBox(height: 16),
FarmCard(
farmName: 'Felda Bukit Ramun',
pondNumber: 3,
alert: 'Voltage Drop',
),
],
),
),
],
),
);
},
),
);
}
Future<String?> _getUserIdAfterDelay(BuildContext context) async {
// Simulate a delay
await Future.delayed(const Duration(milliseconds: 500));
// Ensure this returns the user ID correctly
String? userId = await context.read<LoginCubit>().getUserId();
print("User ID retrieved in AlertMyPage: $userId"); // Debug statement
return userId;
}
}
class FarmCard extends StatelessWidget {
final String farmName;
final int pondNumber;
final String alert;
const FarmCard({
required this.farmName,
required this.pondNumber,
required this.alert,
});
@override
Widget build(BuildContext context) {
return Container(
height: 105,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(15),
color: const Color(0xFFfffbfe),
boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.5),
spreadRadius: 2,
blurRadius: 5,
),
],
),
child: Row(
children: [
Expanded(
flex: 3,
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(farmName,
style: const TextStyle(
fontSize: 18, fontWeight: FontWeight.w700)),
const SizedBox(height: 2),
Text('Pond: $pondNumber',
style: const TextStyle(fontSize: 15)),
Text('Alert: $alert', style: const TextStyle(fontSize: 15)),
],
),
),
),
const Expanded(
flex: 1,
child: Padding(
padding: EdgeInsets.only(left: 19.0),
child: Icon(Icons.flash_on, color: Colors.black),
),
),
],
),
);
}
}
调试控制台:
/颤振(5809):响应代码:200
I/flutter(5809):响应正文:{“userDetails”:{“Name”:“Test 开发商 50","电子邮件":"[电子邮件受保护]","令牌":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjcyNDM0NzU4NDQyMzUxNDExMjAiLCJlbWFpbCI6InRlc3RkZXZlbG9wZXI1MEBnbWFpbC 5jb20iLCJpYXQiOjE3MjczNjc2ODgsImV4cCI6MTcyNzM4OTI4OH0.35ysGilTolHb7iYFUjb1zy8P3qiU-WR0TnDAak8XlKo","描述":"日志 成功了!”}}
I/flutter(5809):解码后的令牌:{id:7243475844235141120,电子邮件: [电子邮件受保护],iat:1727367688,exp:1727389288}
I/flutter(5809):已保存的用户ID:7243475844235141120
我/颤振(5809):令牌已保存: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjcyNDM0NzU4NDQyMzUxNDExMjAiLCJlbWFpbCI6InRlc3RkZXZlbG9wZXI1MEBnbWFpbC5jb20iLCJpYXQiOjE3MjczNjc2ODgsImV 4cCI6MTcyNzM4OTI4OH0.35ysGilTolHb7iYFUjb1zy8P3qiU-WR0TnDAak8XlKo
D/EGL_emulation(5809):app_time_stats:平均= 26.26毫秒,分钟= 2.33毫秒 最大值=445.00ms 计数=32
W/WindowOnBackDispatcher(5809):OnBackInvokedCallback未启用 申请。
W/WindowOnBackDispatcher(5809):设置 应用程序中的“android:enableOnBackInvokedCallback=”true”” 明显。
I/ImeTracker(5809): com.example.kms:b59ac922: onRequestHide at ORIGIN_CLIENT_HIDE_SOFT_INPUT 原因 HIDE_SOFT_INPUT
I/flutter ( 5809):检索到用户 ID:
I/flutter (5809):在 AlertMyPage 中检索到的用户 ID:
问题是您多次创建 SharedPreferences 实例。您只需要一个实例即可保存数据并从中获取数据。 解决办法如下:
import 'package:shared_preferences/shared_preferences.dart';
class SharedPrefService {
// Private constructor
SharedPrefService._privateConstructor();
// Singleton instance
static final SharedPrefService _instance = SharedPrefService._privateConstructor();
// Factory constructor
factory SharedPrefService() {
return _instance;
}
// SharedPreferences instance
static SharedPreferences? _preferences;
// Initialize the SharedPreferences instance
static Future<void> init() async {
_preferences = await SharedPreferences.getInstance();
}
// Save data (String example)
Future<void> saveString(String key, String value) async {
await _preferences?.setString(key, value);
}
// Get data (String example)
String? getString(String key) {
return _preferences?.getString(key);
}
// Remove data
Future<void> remove(String key) async {
await _preferences?.remove(key);
}
}
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await SharedPrefService.init(); // Initialize SharedPreferences
runApp(MyApp());
}
class LoginCubit extends Cubit<LoginState> {
final LoginUserUseCase loginUserUseCase;
LoginCubit({required this.loginUserUseCase}) : super(LoginInitial());
final SharedPrefService _sharedPrefService = SharedPrefService();
Future<void> loginUser({
required String email,
required String password,
}) async {
emit(LoginLoading());
try {
final Either<Failure, User?> result = await loginUserUseCase(
email: email,
password: password,
);
result.fold(
(failure) {
emit(LoginError(message: _mapFailureToMessage(failure)));
},
(user) async {
if (user != null && user.token != null) {
// Decode the JWT token to extract the user ID
String userId = _extractUserIdFromToken(user.token!);
// Save the user token and userId to SharedPreferences
await _saveUserCredentials(user.token!, userId);
emit(LoginLoaded(user: user));
} else {
emit(LoginError(message: 'Failed to retrieve user.'));
}
},
);
} catch (e) {
emit(LoginError(message: e.toString()));
}
}
// Function to decode the JWT token and extract the user ID
String _extractUserIdFromToken(String token) {
Map<String, dynamic> decodedToken = JwtDecoder.decode(token);
print("Decoded token: $decodedToken"); // Debug print
return decodedToken['id']; // Extract the 'id' from the decoded token
}
// Save token and userId to SharedPreferences
Future<void> _saveUserCredentials(String token, String userId) async {
// Remove previous token and user ID if they exist
await _sharedPrefService.remove('user_token');
await _sharedPrefService.remove('user_id');
// Save new credentials
await _sharedPrefService.saveString('user_token', token);
await _sharedPrefService.saveString('user_id', userId);
// Verify successful save
print("User ID saved: $userId");
print("Token saved: $token");
}
// Retrieve userId from SharedPreferences
Future<String?> getUserId() async {
String? userId = _sharedPrefService.getString('user_id');
if (userId == null) {
print("No User ID found in SharedPreferences");
} else {
print("User ID retrieved: $userId");
}
return userId;
}
// Map the failure to a user-friendly error message
String _mapFailureToMessage(Failure failure) {
if (failure is ServerFailure) {
return 'Server error occurred. Please try again later.';
} else if (failure is NetworkFailure) {
return 'Please check your internet connection.';
} else {
return 'Unexpected error occurred. Please try again.';
}
}
}