Flutter项目LoginPage卡在加载界面,添加PIN后问题仍存求助
Flutter登录页面卡在加载界面的问题排查与修复
问题描述
我用Dart编写了Flutter项目代码,但LoginPage始终卡在加载界面,其余功能均正常。学习Flutter已有一个多月,无法诊断出问题所在。即使添加了PIN功能后,该页面仍卡在加载界面,页面中央始终显示加载转圈的指示器。相关代码如下:
import 'dart:convert'; import 'dart:io'; import 'package:vault/login_page.dart'; import 'package:flutter/material.dart'; import 'package:image_picker/image_picker.dart'; import 'package:shared_preferences/shared_preferences.dart'; void main() { WidgetsFlutterBinding.ensureInitialized(); runApp(MyApp()); } class PasswordData { final String website; final String username; final String password; PasswordData(this.website, this.username, this.password); Map<String, dynamic> toJson() { return { 'website': website, 'username': username, 'password': password, }; } factory PasswordData.fromJson(Map<String, dynamic> json) { return PasswordData( json['website'], json['username'], json['password'], ); } } class MyApp extends StatefulWidget { @override _MyAppState createState() => _MyAppState(); } class _MyAppState extends State<MyApp> { bool _isLoggedIn = false; bool _hasPin = false; String _pin = ''; void _handleLogin(String pin) { setState(() { _isLoggedIn = true; _hasPin = true; _pin = pin; }); } @override void initState() { super.initState(); _loadPin(); } Future<void> _loadPin() async { final prefs = await SharedPreferences.getInstance(); setState(() { _hasPin = prefs.getBool('hasPin') ?? false; _pin = prefs.getString('pin') ?? ''; }); } @override Widget build(BuildContext context) { return MaterialApp( // ... (Theme and other configurations) home: DefaultTabController( length: 4, child: Scaffold( appBar: AppBar( // ... (AppBar configuration) ), body: TabBarView( children: [ Tab1Content( isLoggedIn: _isLoggedIn, onLogin: _handleLogin, hasPin: _hasPin, pin: _pin, ), Tab2Content(), Tab3Content(), Tab4Content(), ], ), ), ), ); } } class Tab1Content extends StatefulWidget { final bool isLoggedIn; final void Function(String) onLogin; final bool hasPin; final String pin; Tab1Content({ required this.isLoggedIn, required this.onLogin, required this.hasPin, required this.pin, }); @override _Tab1ContentState createState() => _Tab1ContentState(); } class _Tab1ContentState extends State<Tab1Content> { final _formKey = GlobalKey<FormState>(); final _websiteController = TextEditingController(); final _usernameController = TextEditingController(); final _passwordController = TextEditingController(); List<PasswordData> _passwords = []; Future<void> _addPassword() async { if (_formKey.currentState!.validate()) { final passwordData = PasswordData( _websiteController.text, _usernameController.text, _passwordController.text, ); await _savePassword(passwordData); setState(() { _passwords.add(passwordData); _websiteController.clear(); _usernameController.clear(); _passwordController.clear(); }); } } Future<void> _deletePassword(int index) async { setState(() { _passwords.removeAt(index); }); await _savePasswordsToSharedPreferences(); } Future<void> _savePasswordsToSharedPreferences() async { final prefs = await SharedPreferences.getInstance(); final passwordsList = _passwords.map((password) => password.toJson()).toList(); await prefs.setString('passwords', json.encode(passwordsList)); } Future<void> _savePassword(PasswordData passwordData) async { final prefs = await SharedPreferences.getInstance(); final passwordsJson = prefs.getString('passwords') ?? '[]'; final passwordsList = List<Map<String, dynamic>>.from(json.decode(passwordsJson)); passwordsList.add(passwordData.toJson()); await prefs.setString('passwords', json.encode(passwordsList)); } @override Widget build(BuildContext context) { if (!widget.isLoggedIn) { return Center( child: CircularProgressIndicator(), ); } else { return Column( children: [ Form( key: _formKey, child: Column( children: [ TextFormField( controller: _websiteController, decoration: InputDecoration(labelText: 'Website/Platform'), validator: (value) { if (value?.isEmpty ?? true) { return 'Please enter the website/platform'; } return null; }, ), // ... (similar input fields for username and password) ElevatedButton( onPressed: _addPassword, child: Text('Save Password'), ), ], ), ), Expanded( child: ListView.builder( itemCount: _passwords.length, itemBuilder: (context, index) { final password = _passwords[index]; return ListTile( title: Text(password.website), subtitle: Text('Username: ${password.username}'), trailing: Row( mainAxisSize: MainAxisSize.min, children: [ IconButton( onPressed: () {}, icon: Icon(Icons.copy), ), IconButton( onPressed: () { _deletePassword(index); }, icon: Icon(Icons.delete), ), ], ), ); }, ), ), ], ); } } // ... (other methods, such as _deletePassword, _savePasswordsToSharedPreferences, etc.) } Future<void> _savePassword(PasswordData passwordData) async { final prefs = await SharedPreferences.getInstance(); final passwordsJson = prefs.getString('passwords') ?? '[]'; final passwordsList = List<Map<String, dynamic>>.from(json.decode(passwordsJson)); passwordsList.add(passwordData.toJson()); await prefs.setString('passwords', json.encode(passwordsList)); } @override Widget build(BuildContext context) { // TODO: implement build throw UnimplementedError(); } class Tab2Content extends StatefulWidget { @override _Tab2ContentState createState() => _Tab2ContentState(); } class _Tab2ContentState extends State<Tab2Content> { List<File> _images = []; @override Widget build(BuildContext context) { // TODO: implement build throw UnimplementedError(); } // ... (rest of the code remains the same) } class Tab3Content extends StatelessWidget { @override Widget build(BuildContext context) { return Text('Tab 3'); } } class Tab4Content extends StatefulWidget { @override State<Tab4Content> createState() => _Tab4ContentState(); } class _Tab4ContentState extends State<Tab4Content> { @override Widget build(BuildContext context) { return const Placeholder(); } }
问题排查
从代码中可以定位到几个核心问题:
- 登录触发逻辑缺失:
_isLoggedIn初始值为false,只有调用_handleLogin才能切换为true,但Tab1Content未登录时直接显示加载指示器,没有提供任何让用户输入PIN、触发登录流程的界面,导致登录状态永远无法更新,页面一直卡在加载。 - PIN逻辑未闭环:
_loadPin从本地存储加载了_hasPin状态,但既没有在已有PIN时显示验证界面,也没有在无PIN时显示设置界面,完全没有利用这个状态推进登录流程。 - 代码冗余与错误:存在重复的
_savePassword方法,还有未实现的build方法直接抛出异常,虽然不直接影响登录页面,但会导致代码编译或运行时隐患。
修复方案
1. 替换未登录状态的加载界面为PIN交互界面
修改Tab1Content的build方法,让未登录时显示PIN输入界面,允许用户验证或设置PIN:
@override Widget build(BuildContext context) { if (!widget.isLoggedIn) { return Center( child: Column( mainAxisSize: MainAxisSize.min, children: [ Text(widget.hasPin ? "请输入PIN" : "请设置PIN"), const SizedBox(height: 16), TextField( obscureText: true, keyboardType: TextInputType.number, maxLength: 4, decoration: const InputDecoration( border: OutlineInputBorder(), hintText: "输入4位数字", ), onSubmitted: (value) { if (value.length != 4) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text("请输入4位PIN")), ); return; } if (widget.hasPin) { // 验证已有PIN if (value == widget.pin) { widget.onLogin(value); } else { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text("PIN错误,请重试")), ); } } else { // 保存新PIN并登录 _savePin(value); widget.onLogin(value); } }, ), ], ), ); } else { // 原有的已登录界面代码 return Column( children: [ Form( key: _formKey, child: Column( children: [ TextFormField( controller: _websiteController, decoration: const InputDecoration(labelText: '网站/平台'), validator: (value) { if (value?.isEmpty ?? true) { return '请输入网站/平台名称'; } return null; }, ), // 补充用户名、密码输入框 TextFormField( controller: _usernameController, decoration: const InputDecoration(labelText: '用户名'), validator: (value) { if (value?.isEmpty ?? true) { return '请输入用户名'; } return null; }, ), TextFormField( controller: _passwordController, decoration: const InputDecoration(labelText: '密码'), obscureText: true, validator: (value) { if (value?.isEmpty ?? true) { return '请输入密码'; } return null; }, ), ElevatedButton( onPressed: _addPassword, child: const Text('保存密码'), ), ], ), ), Expanded( child: ListView.builder( itemCount: _passwords.length, itemBuilder: (context, index) { final password = _passwords[index]; return ListTile( title: Text(password.website), subtitle: Text('用户名: ${password.username}'), trailing: Row( mainAxisSize: MainAxisSize.min, children: [ IconButton( onPressed: () {}, icon: const Icon(Icons.copy), ), IconButton( onPressed: () => _deletePassword(index), icon: const Icon(Icons.delete), ), ], ), ); }, ), ), ], ); } } // 添加保存PIN到本地的方法 Future<void> _savePin(String pin) async { final prefs = await SharedPreferences.getInstance(); await prefs.setBool('hasPin', true); await prefs.setString('pin', pin); }
2. 完善登录状态更新逻辑
在MyApp的_handleLogin中,确保PIN状态与本地存储同步:
void _handleLogin(String pin) { setState(() { _isLoggedIn = true; _hasPin = true; _pin = pin; }); }
3. 清理冗余代码
删除重复的_savePassword方法和未实现的build方法,确保代码结构整洁,避免不必要的异常。
内容的提问来源于stack exchange,提问作者beqa ksovr
相关产品推荐
相关产品推荐

