30天计划第16天-Web端适配

-
-
2025-11-21 12:35
  • 学习内容

    1. Flutter Web 配置:pubspec.yaml 启用 Web 支持(Flutter新版本已经支持了,无需配置)

    2. Web 特有问题:鼠标事件(替代触屏)、滚动条优化

  • 实践任务

    1. 将之前的 “登录页 + 首页” 运行到 Web 端(flutter run -d chrome

    2. 适配 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),),
                       )
                      )
                    )
                  ],
                ),
              ),
            
            ),
           ),
        );
      }
    }
  • 运行效果


目录