网络请求打通,ws 打通

This commit is contained in:
Cody
2026-03-09 19:05:55 +08:00
parent 997d821447
commit 3c1976b343
60 changed files with 1392 additions and 552 deletions

View File

@@ -1,45 +1,74 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../../../../core/ui/base/context_theme_ext.dart';
import '../presentation/login_view_model.dart';
import 'package:im_app/features/login/presentation/login_state.dart';
import 'package:im_app/features/login/presentation/login_view_model.dart';
import 'package:im_app/features/login/view/widgets/login_otp_step.dart';
import 'package:im_app/features/login/view/widgets/login_phone_step.dart';
/// 登录页Demo
/// 登录页 — 两步流程:手机号 → 验证码
///
/// 演示 go_router 登录守卫:点击「登录」后经由 [LoginViewModel.demoLogin]
/// 触发 [GoRouter.refreshListenable],守卫重新执行并重定向到 /chat。
/// 步骤 1 [LoginStep.phone][LoginPhoneStep] — 输入国家代码 + 手机号
/// 步骤 2 [LoginStep.otp][LoginOtpStep] — 输入验证码完成登录
///
/// 正式实现时替换为完整登录流程email/password 输入 → LoginViewModel.login
class LoginPage extends ConsumerWidget {
/// 页面本身只持有两个 TextEditingController 和三个回调方法,
/// 具体 UI 由 widgets/ 下的子组件负责。
class LoginPage extends ConsumerStatefulWidget {
const LoginPage({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
// ref.watch 保持 loginViewModelProvider 存活AutoDispose 需要至少一个监听者)
ConsumerState<LoginPage> createState() => _LoginPageState();
}
class _LoginPageState extends ConsumerState<LoginPage> {
// demo 预填,上线前去掉
final _phoneCtrl = TextEditingController(text: '83465308');
final _otpCtrl = TextEditingController(text: '0000');
@override
void dispose() {
_phoneCtrl.dispose();
_otpCtrl.dispose();
super.dispose();
}
void _sendOtp(LoginState state) {
ref
.read(loginViewModelProvider.notifier)
.sendOtp(state.countryCode, _phoneCtrl.text.trim());
}
void _verifyAndLogin() {
ref
.read(loginViewModelProvider.notifier)
.verifyAndLogin(_otpCtrl.text.trim());
}
void _backToPhone() {
_otpCtrl.clear();
ref.read(loginViewModelProvider.notifier).backToPhone();
}
@override
Widget build(BuildContext context) {
final state = ref.watch(loginViewModelProvider);
final s = context.styles;
return Scaffold(
appBar: AppBar(title: const Text('登录'), automaticallyImplyLeading: false),
body: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Text('IM_Demo', style: s.titleMedium),
const SizedBox(height: 8),
Text(
'未登录时任意路由均被重定向到此页 \n 主要是为了展示路由守卫的功能 \n 后续路由守卫专门处理各种跳转前的逻辑判断',
style: s.bodySmall,
),
const SizedBox(height: 32),
FilledButton(
onPressed: state.isLoading
? null
: () => ref.read(loginViewModelProvider.notifier).demoLogin(),
child: const Text('登录'),
),
],
),
appBar: AppBar(automaticallyImplyLeading: false, title: const Text('登录')),
body: Padding(
padding: const EdgeInsets.symmetric(horizontal: 32),
child: state.step == LoginStep.phone
? LoginPhoneStep(
phoneCtrl: _phoneCtrl,
state: state,
onSendOtp: () => _sendOtp(state),
)
: LoginOtpStep(
otpCtrl: _otpCtrl,
state: state,
onVerifyAndLogin: _verifyAndLogin,
onBackToPhone: _backToPhone,
),
),
);
}