Flutter REST API 登录后无法检索用户 ID

问题描述 投票:0回答:1

我对在 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:

flutter dart error-handling bloc
1个回答
0
投票

问题是您多次创建 SharedPreferences 实例。您只需要一个实例即可保存数据并从中获取数据。 解决办法如下:

  1. 创建一个单独的类来处理 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);
  }
}

  1. 在主功能中初始化共享首选项
void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await SharedPrefService.init(); // Initialize SharedPreferences
  runApp(MyApp());
}
  1. 以你肘,你可以这样使用。
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.';
    }
  }
}

© www.soinside.com 2019 - 2024. All rights reserved.