| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183 |
- import 'package:flutter/material.dart';
- import 'package:provider/provider.dart';
- import '../../services/auth_service.dart';
-
- /// 登录页 —— Material Design 3 风格
- class LoginPage extends StatefulWidget {
- const LoginPage({super.key});
- @override
- State<LoginPage> createState() => _LoginPageState();
- }
-
- class _LoginPageState extends State<LoginPage> {
- final _formKey = GlobalKey<FormState>();
- final _userCtrl = TextEditingController(text: 'admin');
- final _passCtrl = TextEditingController(text: 'admin123');
- bool _obscure = true;
- bool _loading = false;
- String? _errorText;
-
- Future<void> _login() async {
- if (!_formKey.currentState!.validate()) return;
-
- setState(() {
- _loading = true;
- _errorText = null;
- });
-
- final auth = context.read<AuthService>();
- final ok = await auth.login(_userCtrl.text.trim(), _passCtrl.text);
-
- if (!mounted) return;
- setState(() => _loading = false);
-
- if (!ok) {
- setState(() => _errorText = '用户名或密码错误,请重试');
- }
- // 成功时 AuthService.notifyListeners 会自动触发 MaterialApp 切换到 HomePage
- }
-
- @override
- void dispose() {
- _userCtrl.dispose();
- _passCtrl.dispose();
- super.dispose();
- }
-
- @override
- Widget build(BuildContext context) {
- final theme = Theme.of(context);
- final colorScheme = theme.colorScheme;
-
- return Scaffold(
- body: SafeArea(
- child: Center(
- child: SingleChildScrollView(
- padding: const EdgeInsets.symmetric(horizontal: 32),
- child: Form(
- key: _formKey,
- child: Column(
- mainAxisSize: MainAxisSize.min,
- children: [
- // ---------- Logo ----------
- Container(
- width: 96,
- height: 96,
- decoration: BoxDecoration(
- color: colorScheme.primaryContainer,
- shape: BoxShape.circle,
- ),
- child: Icon(
- Icons.water_drop,
- size: 48,
- color: colorScheme.onPrimaryContainer,
- ),
- ),
- const SizedBox(height: 24),
-
- // ---------- 标题 ----------
- Text(
- '智慧水务管理系统',
- style: theme.textTheme.headlineSmall?.copyWith(
- fontWeight: FontWeight.bold,
- color: colorScheme.onSurface,
- ),
- ),
- const SizedBox(height: 8),
- Text(
- '请登录您的账号',
- style: theme.textTheme.bodyMedium?.copyWith(
- color: colorScheme.onSurfaceVariant,
- ),
- ),
- const SizedBox(height: 40),
-
- // ---------- 用户名 ----------
- TextFormField(
- controller: _userCtrl,
- textInputAction: TextInputAction.next,
- decoration: InputDecoration(
- labelText: '用户名',
- hintText: '请输入用户名',
- prefixIcon: const Icon(Icons.person_outline),
- border: OutlineInputBorder(
- borderRadius: BorderRadius.circular(12),
- ),
- ),
- validator: (v) => (v == null || v.trim().isEmpty) ? '请输入用户名' : null,
- ),
- const SizedBox(height: 16),
-
- // ---------- 密码 ----------
- TextFormField(
- controller: _passCtrl,
- obscureText: _obscure,
- textInputAction: TextInputAction.done,
- onFieldSubmitted: (_) => _login(),
- decoration: InputDecoration(
- labelText: '密码',
- hintText: '请输入密码',
- prefixIcon: const Icon(Icons.lock_outline),
- suffixIcon: IconButton(
- icon: Icon(_obscure ? Icons.visibility_off : Icons.visibility),
- onPressed: () => setState(() => _obscure = !_obscure),
- ),
- border: OutlineInputBorder(
- borderRadius: BorderRadius.circular(12),
- ),
- ),
- validator: (v) => (v == null || v.isEmpty) ? '请输入密码' : null,
- ),
- const SizedBox(height: 8),
-
- // ---------- 错误提示 ----------
- if (_errorText != null) ...[
- Align(
- alignment: Alignment.centerLeft,
- child: Text(
- _errorText!,
- style: TextStyle(color: colorScheme.error, fontSize: 13),
- ),
- ),
- const SizedBox(height: 8),
- ],
-
- // ---------- 登录按钮 ----------
- const SizedBox(height: 16),
- SizedBox(
- width: double.infinity,
- height: 52,
- child: FilledButton(
- onPressed: _loading ? null : _login,
- style: FilledButton.styleFrom(
- shape: RoundedRectangleBorder(
- borderRadius: BorderRadius.circular(12),
- ),
- ),
- child: _loading
- ? const SizedBox(
- width: 24,
- height: 24,
- child: CircularProgressIndicator(strokeWidth: 2.5, color: Colors.white),
- )
- : const Text('登 录', style: TextStyle(fontSize: 16, fontWeight: FontWeight.w600)),
- ),
- ),
- const SizedBox(height: 24),
-
- // ---------- 底部版本信息 ----------
- Text(
- 'v1.0.0',
- style: theme.textTheme.bodySmall?.copyWith(
- color: colorScheme.onSurfaceVariant.withAlpha(128),
- ),
- ),
- ],
- ),
- ),
- ),
- ),
- ),
- );
- }
- }
|