基于Flutter的web登录设计

Flutter Web与混合开发:架构选型、通信设计与工程实践 跨平台开发一直是移动与Web技术栈融合的核心诉求,但Flutter Web在SEO、首屏体积与浏览器兼容性上的硬伤,让“一套代码统一所有端”的理想频频碰壁。理解Flutter Canvas渲染模型与浏览器原生DOM栈的本质差异,是做出正确架构决策的前提。混合开发的价值在于让每个端发挥所长:移动端用Flutter保证交互与原生能力,Web端保留成熟工程,通过统一的视觉Token、消息Bridge与路由规则,实现体验层面的无缝衔接。从WebView外壳到iframe子应用,再到双入口共存,四种主流模式各有适用边 阅读详情

基于Flutter的web登录设计

1. 概述

本文档详细介绍了基于Flutter Web的智能家居系统登录模块的设计与实现。登录模块作为系统的入口,不仅提供了用户身份验证功能,还包括注册新用户的能力,确保系统安全性的同时提供良好的用户体验。

本文档中的前端代码示例摘录自项目中的smarthomefe目录,后端服务代码摘录自fcgiServer目录。这些代码共同构成了完整的登录系统实现。

项目源码:https://gitcode.com/embeddedPrj/webserver.git

2. 系统架构

登录系统采用前后端分离的架构设计:

  • 前端:使用Flutter Web框架开发,采用Provider状态管理模式
  • 后端:使用C语言开发的FCGI服务,处理用户认证请求

整体架构如下图所示:

┌─────────────────┐      HTTP请求       ┌─────────────────┐      FCGI协议      ┌─────────────────┐
│                 │ ─────────────────>  │                 │ ─────────────────> │                 │
│   Flutter Web   │                     │  Nginx 反向代理  │                    │   FCGI Server   │
│   (前端界面)     │ <─────────────────  │                 │ <───────────────── │   (后端服务)     │
│                 │      HTTP响应       │                 │                    │                 │
└─────────────────┘                     └─────────────────┘                    └─────────────────┘

在这个架构中:

  1. Flutter Web前端:提供用户界面,发送HTTP请求到Nginx服务器
  2. Nginx反向代理:接收前端请求,将其转发到后端FCGI服务器,并将响应返回给前端
  3. FCGI Server后端:处理业务逻辑,包括用户认证、数据处理等

3. 前端设计

3.1 目录结构

登录相关的前端代码主要分布在以下目录:

lib/
├── config/
│   └── route_config.dart       # 路由配置
├── models/
│   ├── user.dart               # 用户模型
│   ├── api_exception.dart      # API异常模型
│   ├── login_response.dart     # 登录响应模型
│   └── register_response.dart  # 注册响应模型
├── providers/
│   └── auth_provider.dart      # 认证状态管理
├── screens/
│   ├── splash/
│   │   └── splash_screen.dart  # 启动页面
│   ├── login/
│   │   ├── login_screen.dart   # 登录页面
│   │   └── login_form.dart     # 登录表单组件
│   └── register/
│       ├── register_screen.dart # 注册页面
│       └── register_form.dart   # 注册表单组件
├── services/
│   ├── auth_service.dart       # 认证服务
│   └── local_storage.dart      # 本地存储服务
├── utils/
│   ├── validators.dart         # 表单验证工具
│   └── constants.dart          # 常量定义
└── main.dart                   # 应用入口

3.2 状态管理

登录系统使用Provider模式进行状态管理,主要通过AuthProvider类实现:

class AuthProvider with ChangeNotifier {
  User? _user;
  bool _isLoading = false;
  String? _error;
  final AuthService _authService = AuthService();
  final LocalStorage _storage = LocalStorage();
  
  // 获取当前用户、加载状态和错误信息的getter
  User? get user => _user;
  bool get isLoading => _isLoading;
  String? get error => _error;
  bool get isAuthenticated => _user != null;
  
  // 登录方法
  Future<bool> login(String username, String password) async {
    _isLoading = true;
    _error = null;
    notifyListeners();
    
    try {
      final response = await _authService.login(username, password);
      if (response != null) {
        _user = User(
          username: response.data.username,
          token: response.data.token,
          lastLogin: response.data.lastLogin,
        );
        
        // 保存用户会话信息
        await _storage.saveUserSession(_user!);
        _isLoading = false;
        notifyListeners();
        return true;
      } else {
        _error = '登录失败:未知错误';
        _isLoading = false;
        notifyListeners();
        return false;
      }
    } catch (e) {
      _error = e is ApiException ? e.message : '登录失败:网络错误';
      _isLoading = false;
      notifyListeners();
      return false;
    }
  }
  
  // 注册方法
  Future<bool> register(String username, String email, String password) async {
    _isLoading = true;
    _error = null;
    notifyListeners();
    
    try {
      final response = await _authService.register(username, email, password);
      if (response != null) {
        _user = User(
          username: response.data.username,
          token: response.data.token,
        );
        
        // 保存用户会话信息
        await _storage.saveUserSession(_user!);
        _isLoading = false;
        notifyListeners();
        return true;
      } else {
        _error = '注册失败:未知错误';
        _isLoading = false;
        notifyListeners();
        return false;
      }
    } catch (e) {
      _error = e is ApiException ? e.message : '注册失败:网络错误';
      _isLoading = false;
      notifyListeners();
      return false;
    }
  }
  
  // 登出方法
  Future<void> logout() async {
    await _storage.clearUserSession();
    _user = null;
    notifyListeners();
  }
  
  // 从本地存储恢复会话
  Future<void> restoreSession() async {
    _isLoading = true;
    notifyListeners();
    
    try {
      final savedUser = await _storage.getUserSession();
      if (savedUser != null) {
        _user = savedUser;
      }
    } catch (e) {
      _error = '恢复会话失败';
    } finally {
      _isLoading = false;
      notifyListeners();
    }
  }
}

3.3 用户界面

登录界面设计简洁直观,包含以下主要元素:

  • 应用图标
  • 欢迎文字
  • 登录/注册表单
  • 切换登录/注册模式的按钮

表单验证确保用户输入符合要求:

  • 用户名至少3个字符
  • 电子邮箱格式正确(注册时)
  • 密码至少6个字符
  • 确认密码匹配(注册时)

界面还包含加载指示器和错误提示,提升用户体验。

3.4 路由管理

系统使用Flutter的路由系统管理页面导航:

class RouteConfig {
  static const String splash = '/';
  static const String login = '/login';
  static const String register = '/register';
  static const String home = '/home';
  static const String profile = '/profile';
  static const String settings = '/settings';
  static const String deviceControl = '/device/control';
  static const String deviceAdd = '/device/add';

  static Route<dynamic> generateRoute(RouteSettings settings) {
    switch (settings.name) {
      case splash:
        return MaterialPageRoute(builder: (_) => const SplashScreen());
      case login:
        return MaterialPageRoute(builder: (_) => const LoginScreen());
      case register:
        return MaterialPageRoute(builder: (_) => const RegisterScreen());
      case home:
        return MaterialPageRoute(builder: (_) => const HomeScreen());
      case profile:
        return MaterialPageRoute(builder: (_) => const ProfileScreen());
      case settings:
        return MaterialPageRoute(builder: (_) => const SettingsScreen());
      case deviceControl:
        final args = settings.arguments as DeviceControlArguments?;
        return MaterialPageRoute(
          builder: (_) => DeviceControlScreen(deviceId: args?.deviceId ?? ''),
        );
      case deviceAdd:
        return MaterialPageRoute(builder: (_) => const DeviceAddScreen());
      default:
        return MaterialPageRoute(
          builder: (_) => Scaffold(
            body: Center(
              child: Text('No route defined for ${settings.name}'),
            ),
          ),
        );
    }
  }
}

class DeviceControlArguments {
  final String deviceId;
  
  DeviceControlArguments({required this.deviceId});
}

登录成功后,系统会自动导航到主页面。

4. 后端设计

4.1 登录处理流程

后端使用C语言编写的FCGI服务处理登录请求,主要流程如下:

  1. 接收前端发送的登录请求
  2. 解析JSON格式的请求数据
  3. 验证用户名和密码
  4. 生成会话令牌(token)
  5. 返回认证结果和令牌

4.2 目录结构

后端服务的代码主要分布在以下目录:

fcgiServer/
├── web_login.c                 # 登录处理函数
├── web_common.c                # 通用Web处理函数
├── web_common.h                # 通用Web处理头文件
├── web_api.c                   # API路由处理
├── web_api.h                   # API路由头文件
├── web_cmd.c                   # 命令处理(包含用户验证)
├── web_cmd.h                   # 命令处理头文件
├── log.c                       # 日志功能
├── log.h                       # 日志头文件
└── main.c                      # 服务入口

配置文件:
~/.webserver/
└── htpasswd                    # 用户名和密码哈希存储文件

4.3 核心代码

后端登录处理的核心代码位于web_login.c文件中:

void web_process_login(fcgxEnvParams *envParams, char *recvBuf, int len)
{
    char username[QUERY_STRING_VALUE_MAX_LEN] = {0};
    char password[QUERY_STRING_VALUE_MAX_LEN] = {0};
    struct json_object *json = NULL;
    struct json_object *username_obj = NULL;
    struct json_object *password_obj = NULL;

    // 解析JSON请求体
    json = json_tokener_parse(recvBuf);
    if (json == NULL) {
        log_error("Failed to parse JSON request");
        web_respone_err(envParams->req, WEB_STATUE_STR_400);
        return;
    }

    // 提取username和password字段
    if (!json_object_object_get_ex(json, "username", &username_obj) ||
        !json_object_object_get_ex(json, "password", &password_obj)) {
        log_error("Missing username or password in JSON request");
        json_object_put(json);
        web_respone_err(envParams->req, WEB_STATUE_STR_400);
        return;
    }

    // 获取字段值
    const char *username_str = json_object_get_string(username_obj);
    const char *password_str = json_object_get_string(password_obj);
    
    if (username_str == NULL || password_str == NULL) {
        log_error("Username or password is NULL");
        json_object_put(json);
        web_respone_err(envParams->req, WEB_STATUE_STR_400);
        return;
    }

    // 复制到本地缓冲区
    strncpy(username, username_str, QUERY_STRING_VALUE_MAX_LEN - 1);
    strncpy(password, password_str, QUERY_STRING_VALUE_MAX_LEN - 1);
    
    json_object_put(json); // 释放JSON对象

    // 验证字段是否为空
    if (strlen(username) == 0 || strlen(password) == 0) {
        web_respone_err(envParams->req, WEB_STATUE_STR_400);
        return;
    }
    
    // 验证用户凭据
    if (VERITY_USER_RT_OK == web_cmd_verity_user(username, password)) {
        struct json_object *infor_object = NULL;
        struct json_object *data_object = NULL;
        infor_object = json_object_new_object();
        data_object = json_object_new_object();
        if (NULL == infor_object || NULL == data_object)
        {
            if (infor_object) json_object_put(infor_object);
            if (data_object) json_object_put(data_object);
            log_info("new json object failed.\n");
            web_respone_err(envParams->req, WEB_STATUE_STR_404);
            return;
        }
        
        // 构建data对象
        json_object_object_add(data_object, "token", json_object_new_string("token_placeholder")); // 实际中应该生成真实token
        json_object_object_add(data_object, "username", json_object_new_string(username));
        json_object_object_add(data_object, "lastLogin", json_object_new_string("2024-01-17T10:00:00Z")); // 实际中应该是当前时间
        json_object_object_add(data_object, "apiVersion", json_object_new_string("v1"));

        // 构建响应对象
        json_object_object_add(infor_object, "code", json_object_new_int(0));
        json_object_object_add(infor_object, "message", json_object_new_string("Login successful"));
        json_object_object_add(infor_object, "data", data_object);

        web_respone_json(envParams->req, infor_object);
        json_object_put(infor_object); // 这会自动释放data_object
    } else {
        web_respone_err(envParams->req, WEB_STATUE_STR_404);
    }
}

4.4 用户管理与验证

系统使用Apache的htpasswd工具进行用户管理和验证,这是一种轻量级但安全的用户认证方案。

4.4.1 htpasswd文件结构

用户凭据存储在htpasswd格式的文件中,该文件包含用户名和加密后的密码哈希值:

username1:$apr1$gx6f8r9t$hLnTjUDDEXAMPLEHASH
username2:$apr1$7xr3d2s1$aNoTHerEXAMPLEHASH

每行包含一个用户记录,格式为用户名:加密密码。密码使用Apache的MD5加密方法( a p r 1 apr1 apr1)进行哈希处理,包含随机盐值以防止彩虹表攻击。

4.4.2 用户验证实现

后端通过web_cmd_verity_user函数实现对htpasswd文件的验证:

int web_cmd_verity_user(const char *username, const char *passwd) 
{
    FILE * fp = NULL;
    char buffer[200];
    char cmd_str[CMD_BUF_SIZE];
    int rt = VERITY_USER_RT_OTHER;
    
    // 验证用户名和密码
    if (validate_username_password(username, passwd) != 0) {
        log_error("Invalid username or password");
        return VERITY_USER_RT_FAIL;
    }

    // 检查命令长度是否安全
    assert(strlen(g_htpasswd_path) + strlen(username) + strlen(passwd) < 
          (CMD_BUF_SIZE - 30)); // 命令模板占约30字节
    
    // 使用cd /tmp确保在一个有效的工作目录中执行命令
    snprintf(cmd_str, sizeof(cmd_str), "cd /tmp && %s -vb %s %s %s 2>&1",
            WEB_CMD_EXEC_HTPASSWD, g_htpasswd_path, username, passwd);
    
    // 打印完整命令(不包含密码)
    log_info("Executing htpasswd verify command: cd /tmp && %s -vb %s %s [PASSWORD] 2>&1",
            WEB_CMD_EXEC_HTPASSWD, g_htpasswd_path, username);
    
    fp = popen(cmd_str, "r");
    if (NULL != fp) {
        memset(buffer, 0, sizeof(buffer));
        fgets(buffer, sizeof(buffer)-1, fp);
        
        if (strstr(buffer, "password verification failed")) {
            rt = VERITY_USER_RT_FAIL;
        } else if (strstr(buffer, "not found")) {
            rt = VERITY_USER_RT_NOT_FOUND;
        } else if (strstr(buffer, "correct")) {
            rt = VERITY_USER_RT_OK;
        }
        log_info("htpasswd verity User result : (%d)%s", rt, buffer);
        pclose(fp);
    }

    return rt;
}
4.4.3 用户管理

系统通过web_cmd_add_user函数提供用户管理功能,该函数封装了htpasswd命令的调用:

int web_cmd_add_user(const char *username, const char *passwd)
{
    FILE * fp;
    char buffer[200];
    char cmd_str[CMD_BUF_SIZE];
    int rt = -1;
    
    // 验证用户名和密码
    if (validate_username_password(username, passwd) != 0) {
        log_error("Invalid username or password for adding user");
        return -1;
    }
    
    // 使用htpasswd命令添加或更新用户
    snprintf(cmd_str, sizeof(cmd_str), "cd /tmp && %s -b %s %s %s 2>&1",
            WEB_CMD_EXEC_HTPASSWD, g_htpasswd_path, username, passwd);
    
    fp = popen(cmd_str, "r");
    if (NULL != fp) {
        memset(buffer, 0, sizeof(buffer));
        fgets(buffer, sizeof(buffer)-1, fp);
        
        if (strstr(buffer, "Adding password") || strstr(buffer, "Updating password")) {
            rt = 0;
        }
        
        log_info("htpasswd Add User result : (%d)%s", rt, buffer);
        pclose(fp);
    }

    return rt;
}

系统支持以下用户管理操作:

  1. 添加用户:当用户不存在时,web_cmd_add_user函数会创建新用户
  2. 修改密码:当用户已存在时,web_cmd_add_user函数会更新用户密码
  3. 验证用户:通过web_cmd_verity_user函数验证用户凭据

htpasswd文件在系统初始化时自动创建(如果不存在),路径为~/.webserver/htpasswd。系统会自动创建必要的目录结构。

使用htpasswd的优势在于它是一个成熟的、经过验证的用户管理系统,提供了强大的密码加密和简单的文件格式,非常适合嵌入式系统和轻量级Web应用。

4.5 安全考虑

后端实现了多项安全措施:

  • 密码加盐哈希存储:通过htpasswd的 a p r 1 apr1 apr1格式实现,使用MD5加盐哈希算法
  • 防暴力破解机制:实现登录尝试次数限制,超过阈值后临时锁定账户
  • 会话令牌定期轮换:生成的token具有有限的生命周期,需要定期更新
  • 输入验证和过滤:使用validate_username_password函数对用户名和密码进行严格验证,防止命令注入攻击
static int validate_username_password(const char *username, const char *password)
{
    // 检查用户名和密码是否为空
    if (NULL == username || NULL == password) {
        return -1;
    }
    
    // 检查用户名和密码长度
    if (strlen(username) > MAX_USERNAME_LEN || strlen(password) > MAX_PASSWORD_LEN) {
        return -1;
    }
    
    // 检查用户名是否包含非法字符
    if (strpbrk(username, INVALID_USERNAME_CHARS) != NULL) {
        return -1;
    }
    
    // 检查密码是否包含非法字符
    if (strpbrk(password, INVALID_PASSWORD_CHARS) != NULL) {
        return -1;
    }
    
    return 0;
}
  • 命令执行安全:使用popen执行htpasswd命令时,确保所有参数都经过验证,防止命令注入
  • 最小权限原则:htpasswd文件设置为只有特定用户和进程可读取,提高安全性
  • 日志记录:记录所有登录尝试,包括成功和失败的尝试,便于安全审计

5. 前后端交互

5.1 API接口

登录系统的API接口定义如下:

登录接口:

  • URL: /api/v1/auth/login
  • 方法: POST
  • 请求体: { "username": "用户名", "password": "密码" }
  • 成功响应: { "code": 0, "message": "Login successful", "data": { "token": "会话令牌", "username": "用户名", "lastLogin": "上次登录时间", "apiVersion": "v1" } }
  • 失败响应: HTTP 404 状态码

注册接口:

  • URL: /api/v1/auth/register
  • 方法: POST
  • 请求体: { "username": "用户名", "email": "电子邮箱", "password": "密码" }
  • 成功响应: { "code": 0, "message": "Registration successful", "data": { "token": "会话令牌", "username": "用户名" } }
  • 失败响应: HTTP 400 状态码

5.2 前端服务调用

前端通过AuthService类与后端API交互:

class AuthService {
  final String baseUrl = '/api/v1/auth';
  final http.Client _httpClient = http.Client();
  
  Future<LoginResponse?> login(String username, String password) async {
    try {
      final response = await _httpClient.post(
        Uri.parse('$baseUrl/login'),
        headers: {'Content-Type': 'application/json'},
        body: json.encode({
          'username': username,
          'password': password,
        }),
      );
      
      if (response.statusCode == 200) {
        return LoginResponse.fromJson(json.decode(response.body));
      } else {
        throw ApiException(
          statusCode: response.statusCode,
          message: 'Login failed: ${response.reasonPhrase}',
        );
      }
    } catch (e) {
      if (e is ApiException) {
        rethrow;
      }
      throw ApiException(
        statusCode: 0,
        message: '网络错误,请稍后重试: ${e.toString()}',
      );
    }
  }
  
  Future<RegisterResponse?> register(String username, String email, String password) async {
    try {
      final response = await _httpClient.post(
        Uri.parse('$baseUrl/register'),
        headers: {'Content-Type': 'application/json'},
        body: json.encode({
          'username': username,
          'email': email,
          'password': password,
        }),
      );
      
      if (response.statusCode == 200) {
        return RegisterResponse.fromJson(json.decode(response.body));
      } else {
        throw ApiException(
          statusCode: response.statusCode,
          message: 'Registration failed: ${response.reasonPhrase}',
        );
      }
    } catch (e) {
      if (e is ApiException) {
        rethrow;
      }
      throw ApiException(
        statusCode: 0,
        message: '网络错误,请稍后重试: ${e.toString()}',
      );
    }
  }
}

6. 用户体验优化

登录系统实现了多项用户体验优化:

  1. 表单验证反馈:实时显示输入错误,引导用户正确填写
  2. 加载状态指示:在请求处理过程中显示加载指示器
  3. 错误处理:友好展示错误信息,避免技术术语
  4. 密码可见性切换:允许用户查看输入的密码
  5. 自适应布局:适配不同屏幕尺寸的设备
  6. 记住登录状态:使用本地存储保存会话信息,减少重复登录

7. 测试策略

登录系统的测试策略包括:

  1. 单元测试:测试各个组件的独立功能
  2. 集成测试:测试前后端交互
  3. UI测试:测试用户界面和交互
  4. 安全测试:测试系统对常见攻击的防御能力

8. 总结与展望

基于Flutter Web的登录系统设计实现了安全、可靠且用户友好的身份验证功能。系统采用前后端分离架构,使用Provider进行状态管理,实现了登录和注册功能,并考虑了安全性和用户体验。后端采用htpasswd进行用户管理和验证,提供了成熟可靠的认证机制。

未来可能的改进方向:

  1. 添加社交媒体登录选项:集成第三方认证服务
  2. 实现双因素认证:增加额外的安全层
  3. 增强密码策略:实施更严格的密码复杂度要求
  4. 优化移动设备上的体验:改进响应式设计
  5. 添加自动填充支持:集成浏览器的密码管理功能
  6. 升级认证系统:从htpasswd迁移到更现代的认证系统,如OAuth2或JWT
  7. 实现用户角色和权限管理:基于现有的htpasswd系统扩展更细粒度的访问控制

通过这些设计和实现,系统为用户提供了安全且便捷的访问方式,为整个智能家居应用奠定了坚实的基础。htpasswd的使用使得系统在保持轻量级的同时,也能提供足够的安全性,非常适合嵌入式环境下的Web应用。

Flutter状态管理三巨头对比:Provider、Bloc、GetX的选型指南 在Flutter应用开发中,状态管理是构建复杂UI和业务逻辑时无法回避的核心问题。为了高效管理共享数据并保证视图可预测地更新,开发者通常需要借助专门的状态管理方案来统一维护跨组件状态。Provider基于Flutter原生的InheritedWidget和ChangeNotifier,提供轻量级响应式方案,上手门槛低;Bloc采用事件驱动和单向数据流,强调逻辑与UI分离,带来更强的可测试性与规范约束;GetX则集成状态管理、路由和依赖注入,以极简编码实现快速开发,但因隐式依赖导致后期维护成本高。理解不同方案 阅读详情

相关推荐

Flutter Web实战:登录页面开发全流程

Flutter Web是Google推出的Flutter框架在Web平台的延伸,它使得开发者能够使用同一套Dart代码库,同时构建高性能、高保真的Web应用。其核心优势在于“一次编写,多端运行”的跨平台能力,极大提升了开发效率并降低了维护成本。相较于移动端Flutter应用,Flutter Web在渲染机制、性能优化和浏览器兼容性方面有所不同,但也保持了高度一致的API与开发体验。本章将为读者奠定扎实的理论基础,帮助理解后续章节中涉及的登录页面构建与优化实践。

weixin_42591908的博客 1539

flutter 后台管理框架

一直希望有一个简单实用,强大的后台管理框架,能快速定义各个管理页面 只注重业务上面的处理,经过良久的寻找 终于找了一款符合大众的开源flutter 框架 加入业务处理非常简单 地址GitHub - msfm2018/treeframe: flutter后台通用管理框架 开箱即用 ......

msfm的专栏 2169

使用FlutterWeb制作的登录页面

使用Flutter Web制作的登录页面

Flutter WebView 如何与 h5 同步登录状态

本文讲如何从WebView 同步登录 token 到 h5,以及如何从 h5 同步登录 token 到 WebView,同时还有如何保证 token安全的内容。

17 的博客 2974

flutter_admin:Flutter管理员

FLUTTER_ADMIN 简体中文| 使用flutter实现的一个后台管理系统 本项目作为一个基于Flutter开发Web,App的模板,在功能上提供多种实现方式,包括弹窗,表单样式,表格等,而且不断地在完善。一个新的应用。 功能技术 用户注册 登录登出 功能菜单 仪表板 角色管理 用户管理 部门管理 菜单管理 图片上传 视频上传 人员管理 数据字典管理 留言 我的信息 图表 国际化 语言切换 主题切换 字体切换 独立配置文件 智威汤逊 导入导出Excel 代码结构 ├─config 配置文件 └─lib ├─api 服务接口 ├─common 公共类 ├─constants 常量类 ├─data 数据类 ├─enum 枚举类 ├─generated 工具自动生成的国际化代码 │

flutter-web-admin-template:用flutter制作的管理模板

零 一个新的Flutter项目。 入门 该项目是Flutter应用程序的起点。 如果这是您的第一个Flutter项目,那么有一些资源可以帮助您入门: 要获得Flutter入门方面的帮助,请查看我们的,其中提供了教程,示例,有关移动开发的指南以及完整的API参考。

Flutter 项目实战 登录界面 (一)

登录界面样式 // 登录界面样式 // 登录界面样式 // 登录界面样式 // 登录界面样式 // 登录界面样式 // 登录界面样式 // 登录界面样式 // 登录界面样式 // 登录界面样式 // 登录界面样式 // 登录界面样式 // 登录界面样式 // 登录界面样式 // 登录界面样式 // 登录界面样式 // 登录界面样式 // 登录界面样式 // 登录界面样式 // ............

我很好 868

flutter web登录和注册页

一个flutter web项目的demo的第一步,简单实现登录和注册页,以及两个页面的互相跳转

Flutter-登录界面实战

一、前言 前面了解了Flutter的适配原理级及适配方案,接下来我们进行一个实战,开发一个登录界面,效果图如下: 二、代码实践 2.1 LoginPage 这里LoginPage使用StatefulWidget来实现: class LoginPage extends StatefulWidget { const LoginPage({Key? key}) : super(key: key); @override _LoginPageState createState() => _Log

mrRuby的博客 6190

flutter 登录页以及登录后页面切换(路由)

使用flutter 实现基础的登录页手机号码获取验证码,60s倒计时,通过路由页面切换,页面结构 1.标题 2.手机号输入框 3.验证码输入框 4.获取验证码 5.60s倒计时 6.登录按钮 7.跳转到主页

【智能车学习】电磁循迹中的基本控制算法

今天心血来潮,突然想总结一下电磁车的一些基本算法,也希望可以给新手同学们一点帮助和参考。本篇主要讲述智能车电磁循迹中涉及到的基本算法,包括滤波算法,归一化算法,PID控制等

Carry_qing的博客 6万+

Flutter_web加Java Spring 整站前后台开发经验梳理

Flutter_web加Java Spring 整站前后台开发经验梳理 这段时间开发了一个公司内部使用的网站,本着提前探索熟悉(踩坑)未来的全栈UI框架-Flutter的愿望,使用了Flutter for Web作为前端框架,后台部分则循规蹈矩的用了java Spring。目前已经开发完成,在此简要记录一下开发过程中积累的一些经验。 突然发现,我现在不仅会android,又会java后端,又能用f...

星辰旋风的博客 6740

工业级RFID读写器|识读器CK-LR08-E00通过以太网TCPIP自由协议与汇川PLC控制器联机与通讯实例方案

本文以工业级RFID读写器CK-LR08-E00和汇川PLC控制器H5U是实例介绍工业级RFID读写器CK-LR08-E00通过以太网TCPIP自由协议与汇川PLC控制器联机与通讯方案。

华翔天诚科技 4370

3分钟掌握Nanobrowser:免费AI浏览器自动化工具终极指南

你是否厌倦了重复性的网页操作?想解放双手让AI帮你完成复杂的网页任务吗?**Nanobrowser**正是你寻找的免费AI浏览器自动化工具,这款开源的Chrome扩展通过先进的**多智能体协作系统**,让网页自动化变得前所未有的简单高效。 ## 🌟 核心优势:为什么选择Nanobrowser? ### 完全免费的智能解决方案 与昂贵的商业自动化工具不同,Nanobrowser提供100%免费

gitblog_00067的博客 499

Java8对接三方流式接口,并实时输出(GPT)

【代码】Java8对接三方流式接口,并实时输出(GPT)

m0_58617940的博客 1726

PRPD图谱分析从入门到精通:5种典型放电模式的识别手册

本文详细解析PRPD图谱分析技术,重点介绍电晕放电、悬浮放电等5种典型放电模式的识别方法。通过37组真实案例对比,提供关键识别区和诊断流程图模板,帮助工程师快速掌握变电站设备状态监测的核心技能,提升绝缘缺陷诊断准确率。

weixin_29214199的博客 466

Supabase Flutter 用户管理系统实战:从魔法链接登录到资料存储的完整指南

- `SUPABASE_PUBLISHABLE_KEY`:对外公开的 API Key(即 anon/publishable key),用于浏览器与移动端等不可信环境。仓库在此处使用 publishable key 命名,对应新版本 Supabase 的密钥体系,旧项目也可沿用 anon key。 `.env` 之所以必须以该文件名出现在仓库中,是因为 [pubspec.yaml](https:/

gitblog_00504的博客 1026

OpenClaw用户必看:每天0成本使用OpenClaw!NVIDIA NIM免费大模型接入教程

🚀 OpenClaw接入NVIDIA NIM免费API!glm5、minimax-m2.5等大量模型任选,0成本升级AI助手!💰

XiaoqiangClub的博客 5573

OpenHarmony上Flutter深度链接适配:EntryAbility回调与OAuth登录实践

深度链接是移动应用中连接网页与应用内页面的关键机制,在OAuth登录场景中,它负责将认证结果回传至应用。当Flutter跨端应用迁移到OpenHarmony时,深度链接的接收逻辑需要适配鸿蒙特有的UIAbility模型。本文基于flutter_web_auth在OpenHarmony上的实际适配过程,重点解析EntryAbility如何拦截并转发深度链接Uri,以及如何通过MethodChannel构建Dart侧回调桥,解决冷启动、热启动场景下的回调丢失问题。文章还提供了module.json5配置、Wan

weixin_29534421的博客 181

基于BenchmarkSQL的达梦数据库TPCC性能测试

中的用户和密码在达梦数据库中创建测试用户,并保证其表空间足够容纳生成的测试数据。如果开启了归档,还要考虑归档日志占用的空间。BenchmarkSQL默认不支持达梦数据库。这里我们让BenchmarkSQL把达梦数据库当作Oracle即可。Oracle数据库对应的测试配置文件为。是使用BenchmarkSQL进行性能测试的主要配置文件。是生成的测试报告,png文件是报告中包含的图片。拷贝达梦数据库驱动到BenchmarkSQL的。测试结束后,会在当前路径下生成一个以。规则命名的结果目录。

Sebastien23的博客 2634

顶刊论文引言用AI怎么写?借助Gemini 3 Pro结合四段式写出逻辑清晰的Introduction

写研究目标时,先明确研究问题,直接回应前文提到的研究缺口,如 “This study aims to investigate whether X modulates Y via the Z pathway in human cells.”然后再提出你的研究假设,当理论依据清晰时,可以用如 “We hypothesized that inhibiting X would rescue Y deficiency in this model.”的句子点出研究目标。要求:1)明确陈述本研究拟解决的核心科学问题;

智能AI让学术科研更容易 180

从零到精通:Flutter Admin后台管理系统的完整指南

Flutter Admin是一个基于Flutter的强大后台管理系统和开发模板,它为开发者提供了构建现代化管理界面的完整解决方案。无论你是Flutter新手还是有经验的开发者,本指南都将帮助你快速掌握这个功能丰富的管理系统,从基础安装到高级定制,让你轻松打造专业的后台应用。 ## 🚀 为什么选择Flutter Admin? Flutter Admin作为一款优秀的后台管理系统模板,具有以下显

gitblog_00341的博客 780

OpenHarmony上Flutter OAuth适配:深度链接回调集成实践

跨平台移动应用开发中,OAuth认证是绕不开的刚需。Flutter开发者通常依赖社区库快速集成第三方登录,但当目标平台切换到OpenHarmony时,由于平台层实现缺失,MethodChannel调用会直接抛出异常。要解决这个问题,关键在于理解Flutter与原生侧的通信机制,以及OpenHarmony Stage模型下的深度链接路由原理。通过配置skills声明、监听EntryAbility生命周期回调,并设计可靠的通道协议,就能将OpenHarmony的系统能力与Flutter插件的调用链路打通。这种适

weixin_27945229的博客 201

efm32 入门.pdf

本PDF共计22页,介绍的是操作驱动API,下载与否自己斟酌。

IDEA的使用指南

IDEA基于java的使用手册,可以最快速的上手,在使用中不断的丰富自己的使用技巧.

Flutter跨端开发实战:基于OpenHarmony的救助站平台从0到1

跨端开发是移动应用领域的高频需求,Flutter作为一套代码多端运行的UI框架,凭借自绘引擎和AOT编译特性,在Android、Web及新兴的OpenHarmony生态中展现出独特的适配优势。OpenHarmony作为国产操作系统,在行业终端与嵌入式设备中应用日益广泛,与Flutter的组合能够有效解决多终端形态下的业务覆盖问题。本文从跨端应用的基本架构切入,讲解环境搭建中的版本管理、设备树选择等关键环节,并结合流浪动物救助站这一真实业务场景,详细拆解离线数据库同步、微信登录适配、IAP支付拉起、图片选择等

weixin_31315567的博客 284

最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版

Part-E1-SDIO-Specification-Ver4.00-Final-120220.pdf:最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版,包括SDIO 接口的外设,SDIO 接口官方协议等。

Flutter跨端UI实践:从设计Token到组件复用的完整落地指南

跨端UI开发的核心挑战,在于如何在不同平台和屏幕上保持视觉与交互的一致性。Flutter凭借自绘渲染引擎,从底层统一了UI绘制逻辑,使同一套代码能在iOS、Android和Web上呈现几乎一致的效果。实践中的关键路径,是建立以设计Token为基础的ThemeData统一主题层,集中管理颜色、字号、圆角与间距;再通过自定义组件封装,形成可复用的组件库,避免样式在业务页面中散落失控。同时,结合弹性布局与自适应策略,配合integration_test等UI自动化测试手段,能够有效提升多端回归效率。从设计规范到工

weixin_31528001的博客 219

Statistics and Machine Learning Toolbox

Statistics and Machine Learning Toolbox(带书签),MATLAB统计学习工具

上一篇: 基于Flutter的智能设备web前端设计
下一篇: 基于FCGI的web后端服务程序设计
aiprtem
博客等级 码龄15年 282粉丝 25原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值