You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.13 00:39:51