news 2026/3/27 18:40:04

【新手学习笔记Day10】DevEco Studio GitCode口袋工具:毛玻璃二次元主题搭建指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
【新手学习笔记Day10】DevEco Studio GitCode口袋工具:毛玻璃二次元主题搭建指南

前言:本文章是我在看到其他人的文章后的学习笔记,我参照的文章链接在下面:

【学习打卡】day03基于flutter实现鸿蒙应用开发之gitcode口袋工具箱完善版本-CSDN博客https://blog.csdn.net/laoren1998/article/details/155579537?fromshare=blogdetail&sharetype=blogdetail&sharerId=155579537&sharerefer=PC&sharesource=jhp0108&sharefrom=from_link

一、项目核心定位

GitCode口袋工具是基于Flutter开发、适配DevEco Studio的轻量化GitCode平台客户端,核心亮点的是毛玻璃二次元视觉风格,功能覆盖登录验证、动态查看、仓库管理、个人信息展示,整体架构模块化,便于维护与扩展。

二、前期准备

  1. 开发环境配置 - 开发工具:DevEco Studio(需适配Flutter开发插件) - 技术依赖:在项目 pubspec.yaml 中添加核心依赖,支撑网络请求、本地存储等功能: yaml dependencies: flutter: sdk: flutter dio: ^5.7.0 # 网络请求核心库 shared_preferences: ^2.3.0 # 本地存储(令牌管理) flutter_easyloading: ^3.0.5 # 加载状态提示(可选优化) - 权限配置:确保项目开启网络权限,满足GitCode API请求需求。

  2. 项目结构规划

采用模块化拆分思路,明确各目录职责,降低代码耦合度,结构如下:

plaintext

lib/

├── main.dart # 应用入口,初始化主题与根组件

├── constants/ # 常量集中管理(API地址、存储键等)

└── app_constants.dart # 核心常量定义

├── themes/ # 主题配置专属目录

└── app_theme.dart # 毛玻璃二次元主题核心配置

├── services/ # 网络服务层

└── gitcode_service.dart # GitCode API请求封装(单例模式)

├── screens/ # 页面组件目录(核心功能页)

├── login_screen.dart # 令牌登录页

├── main_screen.dart # 底部导航主页面

├── home_screen.dart # 首页动态展示

├── repo_screen.dart # 热门/个人仓库页

└── profile_screen.dart # 个人中心页

└── widgets/ # 自定义复用组件

├── glass_card.dart # 毛玻璃卡片(全局复用核心组件)

└── activity_card.dart # 动态列表卡片组件

三、核心模块实现步骤

  1. 常量定义(constants/app_constants.dart) 集中管理API基础地址、令牌存储键、历史记录限制等常量,避免硬编码,便于统一修改:

    class AppConstants { // GitCode API基础地址 static const String apiBaseUrl = 'https://api.gitcode.com/api/v5'; // 当前令牌本地存储键 static const String tokenStorageKey = 'gitcode_token'; // 历史令牌存储键(新增) static const String tokenHistoryKey = 'gitcode_token_history'; // 历史令牌最大存储数量(新增) static const int maxHistoryCount = 5; }
  2. 毛玻璃二次元主题核心配置(themes/app_theme.dart) 主题是风格落地核心,统一管控颜色、字体、组件样式,重点实现二次元视觉与毛玻璃适配:

    import 'package:flutter/material.dart'; // SPDX-License-Identifier: Apache-2.0 import 'package:flutter/material.dart'; /// 毛玻璃二次元主题配置 class AppTheme { /// 字体大小:超大 static const double fontSizeXXLarge = 24; /// 字体大小:大 static const double fontSizeXLarge = 18; /// 字体大小:中 static const double fontSizeLarge = 16; /// 字体大小:中 static const double fontSizeMedium = 14; /// 字体大小:小 static const double fontSizeSmall = 12; /// 主色调(二次元粉紫色) static const Color primaryColor = Color(0xFF8B5CF6); /// 次要色调(天空蓝) static const Color secondaryColor = Color(0xFF38BDF8); /// 强调色(樱花粉) static const Color accentColor = Color(0xFFEC4899); /// 背景色(浅紫灰) static const Color backgroundColor = Color(0xFFF5F3FF); /// 卡片背景色(半透明白) static const Color cardBackgroundColor = Color(0xC8FFFFFF); /// 文字主色 static const Color primaryTextColor = Color(0xFF1E1B4B); /// 文字次要色 static const Color secondaryTextColor = Color(0xFF64748B); /// 边框颜色 static const Color borderColor = Color.fromARGB(50, 139, 92, 246); /// 阴影效果 static const BoxShadow cardShadow = BoxShadow( color: Color.fromRGBO(139, 92, 246, 0.1), // 主题色阴影 blurRadius: 12, // 模糊半径 offset: Offset(0, 4), // 偏移量 ); /// 圆角大小 static const BorderRadius cardBorderRadius = BorderRadius.all(Radius.circular(16)); /// 构建Material主题 static ThemeData get themeData => ThemeData( // 主色调 primaryColor: primaryColor, // 画布背景色 scaffoldBackgroundColor: backgroundColor, // 卡片主题 cardTheme: CardTheme( color: cardBackgroundColor, elevation: 0, shape: RoundedRectangleBorder(borderRadius: cardBorderRadius), shadowColor: cardShadow.color, ), // AppBar主题 appBarTheme: const AppBarTheme( backgroundColor: Colors.transparent, // 透明背景(配合毛玻璃) foregroundColor: primaryTextColor, // 文字颜色 elevation: 0, // 无阴影 centerTitle: true, // 标题居中 ), // 底部导航栏主题 bottomNavigationBarTheme: BottomNavigationBarThemeData( backgroundColor: cardBackgroundColor, // 半透明背景 selectedItemColor: primaryColor, // 选中颜色 unselectedItemColor: secondaryTextColor, // 未选中颜色 elevation: 8, // 阴影 type: BottomNavigationBarType.fixed, // 固定类型 ), // 文字主题 textTheme: const TextTheme( bodyLarge: TextStyle(color: primaryTextColor), bodyMedium: TextStyle(color: secondaryTextColor), titleLarge: TextStyle( fontWeight: FontWeight.bold, color: primaryTextColor, ), ), // 按钮主题 elevatedButtonTheme: ElevatedButtonThemeData( style: ElevatedButton.styleFrom( backgroundColor: primaryColor, // 按钮背景色 foregroundColor: Colors.white, // 按钮文字色 shape: RoundedRectangleBorder(borderRadius: cardBorderRadius), // 圆角 padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 12), // 内边距 ), ), // 输入框主题 inputDecorationTheme: InputDecorationTheme( filled: true, // 填充背景 fillColor: cardBackgroundColor, // 填充色 border: OutlineInputBorder( borderRadius: cardBorderRadius, // 圆角 borderSide: BorderSide(color: borderColor), // 边框色 ), enabledBorder: OutlineInputBorder( borderRadius: cardBorderRadius, borderSide: BorderSide(color: borderColor), ), focusedBorder: OutlineInputBorder( borderRadius: cardBorderRadius, borderSide: BorderSide(color: primaryColor, width: 2), // 聚焦边框色 ), ), ); }
  3. 毛玻璃卡片组件封装(widgets/glass_card.dart) 全局复用核心组件,通过 BackdropFilter + ImageFilter.blur 实现毛玻璃效果,适配各页面:

    // SPDX-License-Identifier: Apache-2.0 import 'package:flutter/material.dart'; import 'dart:ui'; // 用于ImageFilter(毛玻璃) import '../themes/app_theme.dart'; /// 毛玻璃卡片组件 class GlassCard extends StatelessWidget { /// 卡片子组件 final Widget child; /// 内边距 final EdgeInsets padding; /// 外边距 final EdgeInsets margin; const GlassCard({ Key? key, required this.child, this.padding = const EdgeInsets.all(16), this.margin = const EdgeInsets.only(bottom: 16), }) : super(key: key); @override Widget build(BuildContext context) { return Container( margin: margin, //毛玻璃效果核心:BackdropFilter + ImageFilter.blur child: BackdropFilter( filter: ImageFilter.blur(sigmaX: 10, sigmaY: 10), // 模糊程度 child: Container( padding: padding, decoration: BoxDecoration( color: AppTheme.cardBackgroundColor, // 半透明背景 borderRadius: AppTheme.cardBorderRadius, // 圆角 boxShadow: [AppTheme.cardShadow], // 阴影 border: Border.all( color: Colors.white.withOpacity(0.2), // 白色描边(增强玻璃感) width: 1, ), ), child: child, ), ), ); } }
  4. 网络服务层封装(services/gitcode_service.dart) 采用单例模式封装GitCode API请求,统一处理令牌添加、错误拦截,支撑核心功能:

    // SPDX-License-Identifier: Apache-2.0 import 'package:dio/dio.dart'; import '../constants/app_constants.dart'; /// GitCode API服务类 class GitCodeService { // 单例模式:确保全局只有一个实例 static final GitCodeService _instance = GitCodeService._internal(); factory GitCodeService() => _instance; late Dio dio; // Dio实例,用于网络请求 // 内部构造函数 GitCodeService._internal() { // 初始化Dio dio = Dio( BaseOptions( baseUrl: AppConstants.apiBaseUrl, // API基础地址 connectTimeout: const Duration(seconds: 15), // 连接超时 receiveTimeout: const Duration(seconds: 15), // 接收超时 responseType: ResponseType.json, // 响应类型 contentType: 'application/json; charset=utf-8', // 请求类型 headers: { 'Accept': 'application/json', // 接受JSON格式 }, ), ); // 添加拦截器(日志、错误处理) dio.interceptors.add( InterceptorsWrapper( // 请求拦截 onRequest: (options, handler) { // 如果有令牌,添加到请求头 String? token = _accessToken; if (token != null && token.isNotEmpty) { options.headers['Authorization'] = 'Bearer $token'; } print('🚀 请求: ${options.method} ${options.uri}'); handler.next(options); // 继续请求 }, // 响应拦截 onResponse: (response, handler) { print('✅ 响应: ${response.statusCode} ${response.requestOptions.uri}'); handler.next(response); // 继续处理响应 }, // 错误拦截 onError: (DioException e, handler) { print('❌ 错误: ${e.message}'); print('❌ 响应状态: ${e.response?.statusCode}'); print('❌ 响应体: ${e.response?.data}'); // 统一错误信息 String errorMsg = '网络请求失败'; if (e.response?.statusCode == 404) { errorMsg = 'API端点不存在'; } else if (e.response?.statusCode == 401) { errorMsg = '无效的访问令牌'; } else if (e.type == DioExceptionType.connectionTimeout) { errorMsg = '网络超时,请检查网络连接'; } // 包装错误 e = DioException( requestOptions: e.requestOptions, type: e.type, error: errorMsg, response: e.response, ); handler.next(e); // 继续处理错误 }, ), ); } // 令牌存储(全局变量,实际项目建议使用本地存储) static String? _accessToken; /// 设置访问令牌 void setToken(String token) { _accessToken = token; } /// 清除访问令牌 void clearToken() { _accessToken = null; } /// 获取当前用户信息 Future<Map<String, dynamic>> fetchUserInfo() async { try { final response = await dio.get('/user'); // 请求用户信息 return response.data as Map<String, dynamic>; // 返回数据 } on DioException catch (e) { throw Exception(e.error ?? '获取用户信息失败'); // 抛出异常 } } /// 获取用户仓库列表 Future<List<dynamic>> fetchMyRepositories() async { try { final response = await dio.get( '/user/repos', queryParameters: { 'visibility': 'all', // 所有可见性 'affiliation': 'owner', // 仅自己的仓库 'page': 1, // 页码 'per_page': 20, // 每页数量 }, ); return response.data as List<dynamic>; // 返回仓库列表 } on DioException catch (e) { throw Exception(e.error ?? '获取我的仓库失败'); // 抛出异常 } } /// 获取动态列表 Future<List<dynamic>> fetchActivities() async { try { final response = await dio.get( '/events', queryParameters: {'page': 1, 'per_page': 20}, // 分页参数 ); return response.data as List<dynamic>; // 返回动态列表 } catch (e) { print('⚠️ 动态API调用失败,返回模拟数据: $e'); // 返回模拟数据(防止API不可用导致崩溃) return [ { "id": 1, "type": "PushEvent", "actor": {"login": "user1", "avatar_url": "https://placehold.co/40x40/8B5CF6/ffffff?text=U1"}, "repo": {"name": "user1/repo1"}, "created_at": DateTime.now().subtract(const Duration(hours: 1)).toIso8601String() }, { "id": 2, "type": "WatchEvent", "actor": {"login": "user2", "avatar_url": "https://placehold.co/40x40/38BDF8/ffffff?text=U2"}, "repo": {"name": "user2/repo2"}, "created_at": DateTime.now().subtract(const Duration(hours: 2)).toIso8601String() }, ]; } } /// 获取热门仓库 Future<List<dynamic>> fetchHotRepositories() async { try { final response = await dio.get( '/search/repositories', queryParameters: { 'q': 'stars:>100', // 搜索条件:星数>100 'sort': 'stars', // 按星数排序 'order': 'desc', // 降序 'page': 1, // 页码 'per_page': 10, // 每页数量 }, ); final data = response.data as Map<String, dynamic>; return data['items'] as List<dynamic>; // 返回热门仓库列表 } catch (e) { print('⚠️ 热门仓库API调用失败,返回模拟数据: $e'); // 返回模拟数据 return [ { "id": 101, "name": "flutter-kit", "full_name": "google/flutter-kit", "description": "Flutter 开发工具包", "language": "Dart", "stargazers_count": 5000, "forks_count": 1200, "html_url": "https://gitcode.com/google/flutter-kit" }, ]; } } }
  5. 核心页面实现(screens目录) (1)登录页(login_screen.dart) 令牌验证入口,采用毛玻璃卡片承载表单,适配主题风格,验证通过跳转主页面:

    // SPDX-License-Identifier: Apache-2.0 import 'package:flutter/material.dart'; import '../services/gitcode_service.dart'; import '../themes/app_theme.dart'; import '../widgets/glass_card.dart'; import './main_screen.dart'; /// 登录页面 class LoginScreen extends StatefulWidget { const LoginScreen({Key? key}) : super(key: key); @override State<LoginScreen> createState() => _LoginScreenState(); } class _LoginScreenState extends State<LoginScreen> { /// 令牌输入控制器 final TextEditingController _tokenController = TextEditingController(); /// 加载状态 bool _isLoading = false; /// 错误信息 String? _errorMessage; /// 登录处理函数 void _handleLogin() async { setState(() { _isLoading = true; // 显示加载中 _errorMessage = null; // 清空错误信息 }); final token = _tokenController.text.trim(); // 获取输入的令牌 if (token.isEmpty) { setState(() { _isLoading = false; _errorMessage = '请输入访问令牌'; // 空令牌提示 }); return; } try { // 初始化服务并设置令牌 final service = GitCodeService(); service.setToken(token); // 验证令牌是否有效(调用用户信息API) await service.fetchUserInfo(); // 登录成功,跳转到主页面 if (mounted) { Navigator.of(context).pushReplacement( MaterialPageRoute(builder: (_) => const MainScreen()), ); } } catch (e) { setState(() { _isLoading = false; _errorMessage = e.toString(); // 显示错误信息 }); } } @override Widget build(BuildContext context) { return Scaffold( // 背景渐变(增强二次元风格) body: Container( decoration: const BoxDecoration( gradient: LinearGradient( begin: Alignment.topLeft, end: Alignment.bottomRight, colors: [ Color(0xFFF5F3FF), // 浅紫灰 Color(0xFFE0E7FF), // 浅蓝紫 ], ), ), child: SafeArea( child: Center( child: SingleChildScrollView( padding: const EdgeInsets.all(32.0), child: Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, children: <Widget>[ // Logo区域 GlassCard( margin: const EdgeInsets.only(bottom: 32), padding: const EdgeInsets.all(24), child: Column( children: [ // Logo图标 Container( width: 100, height: 100, decoration: BoxDecoration( color: AppTheme.primaryColor, borderRadius: BorderRadius.circular(20), boxShadow: [AppTheme.cardShadow], ), child: const Icon( Icons.code, size: 60, color: Colors.white, ), ), const SizedBox(height: 16), // 应用标题 const Text( 'GitCode 客户端', style: TextStyle( fontSize: 28, fontWeight: FontWeight.bold, color: AppTheme.primaryTextColor, ), ), const SizedBox(height: 8), // 副标题 const Text( '使用访问令牌登录', style: TextStyle( fontSize: 16, color: AppTheme.secondaryTextColor, ), ), ], ), ), // 登录表单 GlassCard( child: Column( children: [ // 令牌输入框 TextField( controller: _tokenController, obscureText: true, // 密码模式(隐藏令牌) decoration: InputDecoration( labelText: '访问令牌', hintText: 'ghp_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', hintStyle: const TextStyle(color: AppTheme.secondaryTextColor), errorText: _errorMessage, // 错误提示 prefixIcon: const Icon(Icons.security, color: AppTheme.primaryColor), suffixIcon: IconButton( icon: const Icon(Icons.clear, color: AppTheme.secondaryTextColor), onPressed: () => _tokenController.clear(), // 清空输入 ), ), maxLines: 1, textInputAction: TextInputAction.done, onSubmitted: (_) => _handleLogin(), // 回车登录 ), const SizedBox(height: 16), // 帮助链接 Align( alignment: Alignment.centerRight, child: TextButton( onPressed: () { // 打开获取令牌的帮助页面(预留) print('打开获取令牌帮助'); }, child: const Text( '如何获取令牌?', style: TextStyle(color: AppTheme.primaryColor), ), ), ), const SizedBox(height: 24), // 登录按钮 SizedBox( width: double.infinity, height: 50, child: ElevatedButton.icon( onPressed: _isLoading ? null : _handleLogin, // 加载中禁用 icon: _isLoading ? const SizedBox( width: 20, height: 20, child: CircularProgressIndicator(color: Colors.white, strokeWidth: 2), ) : const Icon(Icons.login), label: const Text( '登录', style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold), ), ), ), ], ), ), ], ), ), ), ), ), ); } }

    (2)主页面(main_screen.dart) 底部导航核心页,切换首页、仓库、个人中心,导航栏适配毛玻璃效果:

    import 'dart:ui'; import 'package:flutter/material.dart'; import './home_screen.dart'; import './repo_screen.dart'; import './profile_screen.dart'; import '../themes/app_theme.dart'; class MainScreen extends StatefulWidget { const MainScreen({Key? key}) : super(key: key); @override State<MainScreen> createState() => _MainScreenState(); } class _MainScreenState extends State<MainScreen> { int _selectedIndex = 0; // 当前选中页面索引 static const List<Widget> _widgetOptions = [HomeScreen(), RepoScreen(), ProfileScreen()]; // 页面列表 // 导航栏点击切换 void _onItemTapped(int index) => setState(() => _selectedIndex = index); @override Widget build(BuildContext context) { return Scaffold( // 背景渐变(与登录页一致,风格统一) body: Container( decoration: const BoxDecoration( gradient: LinearGradient(begin: Alignment.topLeft, end: Alignment.bottomRight, colors: [Color(0xFFF5F3FF), Color(0xFFE0E7FF)]), ), child: _widgetOptions.elementAt(_selectedIndex), ), // 毛玻璃底部导航栏 bottomNavigationBar: Container( child: BackdropFilter( filter: ImageFilter.blur(sigmaX: 10, sigmaY: 10), child: BottomNavigationBar( items: const [ BottomNavigationBarItem(icon: Icon(Icons.home_outlined), activeIcon: Icon(Icons.home), label: '首页'), BottomNavigationBarItem(icon: Icon(Icons.code_outlined), activeIcon: Icon(Icons.code), label: '仓库'), BottomNavigationBarItem(icon: Icon(Icons.person_outlined), activeIcon: Icon(Icons.person), label: '我的'), ], currentIndex: _selectedIndex, onTap: _onItemTapped, backgroundColor: AppTheme.cardBackgroundColor, selectedItemColor: AppTheme.primaryColor, unselectedItemColor: AppTheme.secondaryTextColor, elevation: 8, type: BottomNavigationBarType.fixed, ), ), ), ); } }

    (3)首页动态/仓库/个人中心页 均基于 GlassCard 组件复用毛玻璃风格,核心逻辑为:请求接口数据→展示加载/错误/空状态→渲染列表/详情,以个人中心为例(展示用户信息+登出功能):

    import './login_screen.dart'; import 'package:flutter/material.dart'; import '../services/gitcode_service.dart'; import '../themes/app_theme.dart'; import '../widgets/glass_card.dart'; class ProfileScreen extends StatefulWidget { const ProfileScreen({Key? key}) : super(key: key); @override State<ProfileScreen> createState() => _ProfileScreenState(); } class _ProfileScreenState extends State<ProfileScreen> { final GitCodeService _service = GitCodeService(); Map<String, dynamic>? _userInfo; // 用户信息 bool _isLoading = true; // 加载状态 String? _errorMessage; // 错误提示 @override void initState() {super.initState(); _fetchUserInfo();} // 获取用户信息 Future<void> _fetchUserInfo() async { setState(() {_isLoading = true; _errorMessage = null;}); try { final userInfo = await _service.fetchUserInfo(); setState(() => _userInfo = userInfo); } catch (e) { setState(() => _errorMessage = e.toString()); } finally { setState(() => _isLoading = false); } } // 格式化日期(YYYY年MM月DD日) String _formatDate(String? dateStr) => dateStr == null ? '' : DateTime.parse(dateStr).toString().split(' ')[0].replaceAll('-', '年') + '日'; // 构建统计项(仓库/关注者/关注中) Widget _buildStatItem(String label, int count, IconData icon) => Column( children: [ Container(width: 56, height: 56, decoration: BoxDecoration(borderRadius: BorderRadius.circular(16), color: AppTheme.primaryColor.withOpacity(0.1)), child: Icon(icon, size: 28, color: AppTheme.primaryColor)), const SizedBox(height: 8), Text(count.toString(), style: const TextStyle(fontSize: 24, fontWeight: bold, color: AppTheme.primaryTextColor)), const SizedBox(height: 4), Text(label, style: TextStyle(fontSize: AppTheme.fontSizeSmall, color: AppTheme.secondaryTextColor)), ], ); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text('我的'), backgroundColor: Colors.transparent, actions: [IconButton(icon: const Icon(Icons.settings_outlined), onPressed: () => print('打开设置'))]), body: RefreshIndicator( onRefresh: _fetchUserInfo, // 下拉刷新 color: AppTheme.primaryColor, backgroundColor: AppTheme.cardBackgroundColor, child: _isLoading ? const Center(child: Column(mainAxisAlignment: MainAxisAlignment.center, children: [CircularProgressIndicator(color: AppTheme.primaryColor), SizedBox(height: 16), Text('加载中...')])) : _errorMessage != null ? Center( child: GlassCard( child: Column(mainAxisAlignment: MainAxisAlignment.center, children: [ const Icon(Icons.error_outline, size: 64, color: Colors.red), const SizedBox(height: 16), Text(_errorMessage!, textAlign: TextAlign.center, style: const TextStyle(color: AppTheme.secondaryTextColor)), const SizedBox(height: 16), ElevatedButton(onPressed: () {_service.clearToken(); Navigator.of(context).pushReplacement(MaterialPageRoute(builder: (_) => const LoginScreen()));}, child: const Text('重新登录')), ]), ), ) : _userInfo == null ? const Center(child: Text('未能获取用户信息')) : SingleChildScrollView( padding: const EdgeInsets.all(20), child: Column( crossAxisAlignment: CrossAxisAlignment.center, children: [ // 用户信息卡片 GlassCard( margin: const EdgeInsets.only(bottom: 24), padding: const EdgeInsets.all(24), child: Column( crossAxisAlignment: CrossAxisAlignment.center, children: [ CircleAvatar(radius: 60, backgroundImage: NetworkImage(_userInfo!['avatar_url'] ?? ''), backgroundColor: AppTheme.secondaryColor.withOpacity(0.1)), const SizedBox(height: 16), Text(_userInfo!['name'] ?? _userInfo!['login'] ?? '匿名用户', style: const TextStyle(fontSize: AppTheme.fontSizeXXLarge, fontWeight: bold, color: AppTheme.primaryTextColor)), Text('@${_userInfo!['login']}', style: TextStyle(fontSize: AppTheme.fontSizeLarge, color: AppTheme.secondaryTextColor)), const SizedBox(height: 16), if (_userInfo!['bio'] != null) Text(_userInfo!['bio'] ?? '', textAlign: TextAlign.center, style: TextStyle(fontSize: AppTheme.fontSizeMedium, color: AppTheme.secondaryTextColor)), ], ), ), // 统计卡片 GlassCard( margin: const EdgeInsets.only(bottom: 24), padding: const EdgeInsets.all(24), child: Row(mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ _buildStatItem('仓库', _userInfo!['public_repos'] ?? 0, Icons.code_outlined), _buildStatItem('关注者', _userInfo!['followers'] ?? 0, Icons.people_outlined), _buildStatItem('关注中', _userInfo!['following'] ?? 0, Icons.person_add_outlined), ]), ), // 登出按钮(红色主题,区分常规按钮) SizedBox( width: double.infinity, height: 50, child: ElevatedButton.icon( onPressed: () {_service.clearToken(); Navigator.of(context).pushReplacement(MaterialPageRoute(builder: (_) => const LoginScreen()));}, icon: const Icon(Icons.logout), label: const Text('登出'), style: ElevatedButton.styleFrom(backgroundColor: Colors.redAccent, foregroundColor: Colors.white), ), ), ], ), ), ), ); } }
  6. 应用入口(main.dart)

初始化应用,绑定主题配置,设置初始页面为登录页:

// SPDX-License-Identifier: Apache-2.0 import 'package:flutter/material.dart'; import './screens/login_screen.dart'; import './themes/app_theme.dart'; /// 应用入口函数 void main() { runApp(const MyApp()); // 启动应用 } /// 根组件 class MyApp extends StatelessWidget { const MyApp({Key? key}) : super(key: key); @override Widget build(BuildContext context) { return MaterialApp( title: 'GitCode 客户端', // 应用标题 theme: AppTheme.themeData, // 应用主题 home: const LoginScreen(), // 初始页面(登录页) debugShowCheckedModeBanner: false, // 隐藏调试横幅 ); } }

四、毛玻璃二次元风格核心要点

1. 背景基础:全局使用 LinearGradient 浅紫灰→浅蓝紫渐变,奠定柔和二次元基调,避免纯色单调。

2. 毛玻璃核心:通过 BackdropFilter + ImageFilter.blur(sigmaX:10, sigmaY:10) 实现模糊效果,配合半透明白卡片( Color.fromARGB(200, 255, 255, 255) ),强化玻璃质感。

3. 视觉细节:

- 圆角:全局统一16px圆角,弱化尖锐感,契合二次元柔和风格;

- 配色:以粉紫为主色,天蓝、樱花粉为辅,文字区分主次色,视觉统一;

- 阴影:使用主题色淡阴影( opacity:0.1 ),增加组件层次感,不突兀。

4. 组件复用:封装 GlassCard 组件,全局页面复用,确保风格一致性,减少重复代码。

五、运行测试

1. 依赖安装:配置 pubspec.yaml 后,点击DevEco Studio「Pub get」安装依赖;

2. 设备适配:选择模拟器或真实设备,确保设备网络正常;

3. 功能测试:验证登录(令牌有效性)、动态加载、仓库切换、个人信息展示、登出等核心功能,检查毛玻璃效果适配性。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/3/27 0:44:19

GUID为什么不会重复?

GUID为什么不会重复? GUID/UUID &#xff08;全局唯一标识符&#xff09;之所以被认为“几乎不会重复”&#xff0c;是因为其庞大的组合空间和精心设计的生成算法&#xff0c;使得在现实世界中重复的概率低到可以忽略不计。 以下是 GUID 不会重复的核心原因&#xff1a; 1. 庞…

作者头像 李华
网站建设 2026/3/21 15:14:39

E-Hentai批量下载工具:高效管理数字收藏资源的最佳方案

在数字资源日益丰富的今天&#xff0c;如何高效管理和保存有价值的在线内容成为了许多用户的共同痛点。面对心仪的图库资源&#xff0c;传统的手动保存方式不仅效率低下&#xff0c;还容易导致文件混乱。针对这一需求&#xff0c;E-Hentai-Downloader提供了一个简单而强大的解决…

作者头像 李华
网站建设 2026/3/21 11:57:52

布隆过滤器

一、布隆过滤器 1. 什么是布隆过滤器&#xff1f; 布隆过滤器是一种空间效率极高的概率型数据结构&#xff0c;核心作用是快速判断「一个元素是否存在于集合中」。它的特点可以总结为&#xff1a; 说「元素不在」→ 100%准确&#xff08;绝对没在集合里&#xff09;&#xff1b…

作者头像 李华
网站建设 2026/3/24 11:39:12

【JESD22-B109C】倒装芯片拉伸测试

B109C 测试方法&#xff1a;Flip Chip Tensile Pull 倒装芯片拉伸测试1 范围本测试方法适用于芯片与基板焊点形成后、未涂覆底部填充胶或其他会提高表观结合强度的材料前的倒装芯片。其用途包括&#xff1a;评估特定倒装芯片的芯片接合工艺一致性与质量&#xff1b;评估特定倒装…

作者头像 李华
网站建设 2026/3/28 13:53:00

2025年应届生闭坑指南:如何挑选低费用、高认可度的AI技能证书?

随着人工智能技术席卷各行各业&#xff0c;手握相关技能证书已成为应届毕业生提升就业竞争力的重要筹码。然而&#xff0c;面对市场上琳琅满目、价格不一的认证项目&#xff0c;许多同学不禁感到迷茫&#xff1a;如何避开“高价低能”的坑&#xff0c;选择一款既具高含金量又不…

作者头像 李华
网站建设 2026/3/26 5:26:28

基于YOLOv12农作物检测系统1:农作物检测数据集说明(含下载链接)

一. 前言 本篇博客是《基于YOLOv12农作物检测系统》系列文章之《农作物检测数据集说明(含下载链接)》&#xff0c;网上有很多农作物检测数据集的数据&#xff0c;百度一下&#xff0c;一搜一大堆&#xff0c;但质量参差不齐&#xff0c;很多不能用&#xff0c;即使一个一个的看…

作者头像 李华