学习内容:
Flutter Web 配置:pubspec.yaml启用 Web 支持(Flutter新版本已经支持了,无需配置)Web 特有问题:鼠标事件(替代触屏)
、滚动条优化实践任务:
将之前的 “登录页 + 首页” 运行到 Web 端(
flutter run -d chrome)适配 Web 端按钮:鼠标悬浮时改变颜色
一、Flutter Web配置※
在Flutter新的版本(按豆包的说法是2.0之后)已经默认支持Web了,无需配置。
二、鼠标特有问题:鼠标事件(替代触屏)※
Flutter Web应用运行在浏览器中,其输入方式和交互体验与移动设备有本质区别,因此需要针对性地进行优化。
在移动应用中,我们使用GestureDetector来处理onTap, onDoubleTap, onLongPress等触屏事件(Flutter已经在底层实现了,因此开发者无感)。在Web端,这些事件依然有效,Flutter会将鼠标的click事件映射为onTap。
但是,为了提供更符合Web用户习惯的体验,可以使用专门针对鼠标的事件。
核心组件:MouseRegion
MouseRegion是一个用于检测鼠标指针进入、退出或移动的组件。它非常适合实现悬停(Hover)效果。
- 常用属性
- onEnter:当鼠标指针进入组件区域时触发。
- onExit:当鼠标指针离开组件区域时触发。
- onHover:当鼠标指针在组件区域内移动时持续触发。
- cursor:指定鼠标指针在区域内时的样式(如:SystemMouseCursors.click, SystemMouseCursors.text等)。
三、综合实战※
- 前情:登录页+首页源码参考第11天综合实战,在此基础上进行修改。
- 新增功能:
- 鼠标移入和移出登录按钮,按钮颜色变化。
参考代码
import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import '../models/user_model.dart'; import 'home_page.dart'; class LoginPage extends StatefulWidget { const LoginPage({super.key}); @override State<LoginPage> createState() => _LoginPageState(); } class _LoginPageState extends State<LoginPage> { final _formKey = GlobalKey<FormState>(); final _usernameController = TextEditingController(); bool _isButtonHovered = false; // 登录逻辑 void _submitForm() { if (_formKey.currentState!.validate()) { // 1. 获取用户名 String username = _usernameController.text; // 2. 通过Provider获取UserModel实例,调用login方法 Provider.of<UserModel>(context, listen: false).login(username); // 3. 跳转到首页(替换当前路由,避免返回登录页) Navigator.pushReplacement(context, MaterialPageRoute(builder: (context) => const HomePage())); } } @override void dispose() { _usernameController.dispose(); super.dispose(); } @override Widget build(BuildContext context) { final Color buttonColor = Colors.blue; final Color hoverColor = Colors.blueAccent; return Scaffold( backgroundColor: Colors.grey[50], body: Center( child: SingleChildScrollView( padding: const EdgeInsets.symmetric(horizontal: 20), child: Form( key: _formKey, child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ // 应用图标 const Icon( Icons.lock, size: 80, color: Colors.blue, ), const SizedBox(height: 40,), // 账号输入框 TextFormField( controller: _usernameController, keyboardType: TextInputType.text, // 自动获取焦点(进入页面后自动弹出键盘) autofocus: true, decoration: InputDecoration( labelText: '账号', hintText: '请输入账号', prefixIcon: const Icon(Icons.person), border: const OutlineInputBorder( borderRadius: BorderRadius.all(Radius.circular(10)), ), // 聚焦时边框颜色 focusedBorder: OutlineInputBorder( borderSide: const BorderSide(color: Colors.blue, width: 2), borderRadius: BorderRadius.circular(10), ) ), validator: (value) { if (value == null || value.isEmpty) { return '请输入账号'; // 验证失败,返回错误提示 } return null; }, ), const SizedBox(height: 20,), // 登录按钮 MouseRegion( onEnter: (event) { setState(() { _isButtonHovered = true; }); }, onExit: (event) { setState(() { _isButtonHovered = false; }); }, cursor: SystemMouseCursors.click, child: AnimatedContainer( duration: const Duration(milliseconds: 200), color: _isButtonHovered ? hoverColor : buttonColor, child: ElevatedButton( onPressed: _submitForm, style: ElevatedButton.styleFrom( minimumSize: const Size(double.infinity, 50), backgroundColor: Colors.transparent, foregroundColor: Colors.white, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(10), // 圆角 ), side: BorderSide.none, ), child: const Text('登录', style: TextStyle(fontSize: 18, fontWeight: FontWeight.w500),), ) ) ) ], ), ), ), ), ); } }运行效果
