当前位置: 首页 > news >正文

AtomGit Flutter鸿蒙客户端:仓库详情页

路由参数

仓库详情页通过命名路由/repo进入,接收ownername参数:

// 导航到详情页Navigator.pushNamed(context,'/repo',arguments:{'owner':'atomgit','name':'flutter-ohos'});// 详情页提取参数finalargs=ModalRoute.of(context)!.settings.argumentsasMap<String,dynamic>;finalowner=args['owner']asString;finalname=args['name']asString;

RepoDetailProvider

Provider 在创建时即开始加载数据:

classRepoDetailProviderextendsChangeNotifier{finalAtomGitApiClient_apiClient;Repository?_repository;String?_readme;bool _isLoading=false;String?_error;Future<void>load(Stringowner,Stringname)async{_isLoading=true;_error=null;notifyListeners();try{finalencodedOwner=Uri.encodeComponent(owner);finalencodedName=Uri.encodeComponent(name);finalresponse=await_apiClient.get('/repos/$encodedOwner/$encodedName',);finaldata=parseMap(response.data);if(data!=null){_repository=Repository.fromJson(data);}_readme=await_loadReadme(encodedOwner,encodedName,_repository?.defaultBranch??'main');}onApiExceptioncatch(e){_error=e.message;}catch(e){_error='加载失败:$e';}finally{_isLoading=false;notifyListeners();}}}

关键细节:owner 和 name 都经过Uri.encodeComponent编码,处理含特殊字符的仓库路径。

README 加载

README 加载采用静默失败策略 —— 没有 README 不报错,只是不展示:

Future<String?>_loadReadme(Stringowner,Stringrepo,Stringbranch)async{try{finalresponse=await_apiClient.get('/repos/$owner/$repo/readme',queryParams:{'ref':branch},);finaldata=parseMap(response.data);if(data!=null&&data['content']isString){finalcontent=data['content']asString;returnutf8.decode(base64.decode(content));}}onApiException{// README 不存在是正常的,不报错}catch(_){}returnnull;}

API 返回的 README 内容是 Base64 编码的,解码后用MarkdownViewer渲染。

页面结构

RepoDetailScreen 是 StatelessWidget,通过 ChangeNotifierProvider 注入 Provider:

classRepoDetailScreenextendsStatelessWidget{@overrideWidgetbuild(BuildContextcontext){finalargs=ModalRoute.of(context)!.settings.argumentsasMap<String,dynamic>;finalowner=args['owner']asString;finalname=args['name']asString;returnChangeNotifierProvider(create:(_)=>RepoDetailProvider(context.read<AtomGitApiClient>())..load(owner,name),child:_RepoDetailBody(owner:owner,name:name),);}}

自定义 Tab 栏

不使用 Flutter 的 TabBar/TabBarView,而是自定义 Row 按钮实现四个标签切换:

enum_DetailTab{code,issues,pulls,readme}class_RepoDetailBodyStateextendsState<_RepoDetailBody>{_DetailTab _currentTab=_DetailTab.readme;Widget_buildTabBar(){returnMaterial(elevation:1,child:Padding(padding:constEdgeInsets.symmetric(horizontal:16,vertical:8),child:Row(children:[_TabButton(label:'代码',isSelected:_currentTab==_DetailTab.code,onTap:(){Navigator.pushNamed(context,'/repo/code',arguments:{'owner':widget.owner,'name':widget.name,'branch':provider.repository?.defaultBranch??'main',});},),_TabButton(label:'Issues',isSelected:_currentTab==_DetailTab.issues,onTap:(){Navigator.pushNamed(context,'/repo/issues',arguments:{'owner':widget.owner,'name':widget.name,'type':'issue',});},),_TabButton(label:'PRs',isSelected:_currentTab==_DetailTab.pulls,onTap:(){Navigator.pushNamed(context,'/repo/pulls',arguments:{'owner':widget.owner,'name':widget.name,'type':'pr',});},),_TabButton(label:'README',isSelected:_currentTab==_DetailTab.readme,onTap:()=>setState(()=>_currentTab=_DetailTab.readme),),],),),);}}

每个按钮的选中态通过底部蓝色指示条实现:

class_TabButtonextendsStatelessWidget{finalStringlabel;finalbool isSelected;finalVoidCallbackonTap;Widgetbuild(BuildContextcontext){returnExpanded(child:GestureDetector(onTap:onTap,child:Column(children:[Text(label,style:TextStyle(color:isSelected?Theme.of(context).colorScheme.primary:null,fontWeight:isSelected?FontWeight.w600:null,)),if(isSelected)Container(height:2,margin:constEdgeInsets.only(top:4),color:Theme.of(context).colorScheme.primary,),],),),);}}

头部信息区

仓库名、描述、统计数据以卡片形式展示:

Widget_buildRepoHeader(Repositoryrepo){returnCard(margin:constEdgeInsets.all(16),child:Padding(padding:constEdgeInsets.all(16),child:Column(crossAxisAlignment:CrossAxisAlignment.start,children:[Row(children:[Icon(repo.isPrivate?Icons.lock_outline:Icons.book_outlined,size:20),constSizedBox(width:8),Expanded(child:Text(repo.fullName,style:Theme.of(context).textTheme.titleMedium)),]),if(repo.description!=null)...[constSizedBox(height:8),Text(repo.description!),],constSizedBox(height:12),Row(children:[_StatItem(Icons.star_border,'${repo.stargazersCount}'),_StatItem(Icons.call_split,'${repo.forksCount}'),_StatItem(Icons.remove_red_eye_outlined,'${repo.watchersCount}'),_StatItem(Icons.error_outline,'${repo.openIssuesCount}'),]),],),),);}

README 渲染

Widget_buildReadme(RepoDetailProviderprovider){if(provider.readme==null||provider.readme!.isEmpty){returnconstCenter(child:Text('暂无 README'));}returnSingleChildScrollView(padding:constEdgeInsets.all(16),child:MarkdownViewer(markdown:provider.readme!),);}

状态处理

三种状态的完整处理:

Widget_buildBody(RepoDetailProviderprovider){if(provider.error!=null&&provider.repository==null){returnErrorRetryWidget(message:provider.error!,onRetry:()=>provider.load(widget.owner,widget.name),);}if(provider.repository==null){returnconstLoadingIndicator(message:'加载仓库信息...');}returnColumn(children:[_buildTabBar(),Expanded(child:_buildReadme(provider)),]);}

错误时展示可重试的错误页面,加载中展示 LoadingIndicator,成功后展示完整内容。

http://www.jsqmd.com/news/969522/

相关文章:

  • 探索ComfyUI-KJNodes的3个核心维度:从模块化思维到创意实践
  • Windows安卓应用安装终极方案:APK Installer五分钟快速上手指南
  • 普林斯顿团队发布Goedel - Architect:低成本开源框架革新形式化定理证明
  • CSDN AI数字营销免费试用期到底几天?3大关键限制+2个自动续费陷阱,90%新人不知道
  • 2026年6月7日博客精选
  • ADC精度与分辨率深度解析:从概念到选型实战指南
  • 前端和测试岗想转AI,你的工程经验其实是张好牌
  • Linux内核时间管理与延时机制:从jiffies到高精度定时器实战
  • I2C软件模拟驱动开发:从协议原理到稳定调试的实战指南
  • Android 13应用语言独立设置:打破系统限制的技术实现方案
  • 终极抖音下载指南:如何免费批量保存视频、图集和直播回放
  • ArchivePasswordTestTool:基于7zip引擎的企业级加密压缩包密码恢复解决方案架构与实践
  • 现代 Web 高吞吐状态流转:基于发布订阅(Pub/Sub)模式与 Proxy 数据双向绑定手写高性能状态管理器
  • 2026年阿里云OpenClaw/Hermes Agent配置Token Plan安装步骤全解
  • DataCleaner 5.1.5 全功能开源数据清洗套件:可视化操作+命令行支持+多源接入+脚本扩展
  • LabVIEW嵌入式开发:从图形化编程到实时控制与FPGA硬件实现
  • 终极指南:如何使用TegraRcmGUI图形化工具轻松完成Switch RCM注入
  • WinForm拖拽即用的DataGridView分页控件(带源码和完整示例)
  • 如何快速掌握Jupyter AI:新手到专家的完整实战指南
  • 2026年深圳小程序商城制作哪家好
  • ComfyUI IPAdapter终极指南:3分钟掌握AI图像风格迁移
  • 2026年国内气凝胶毡/纳米气凝胶毡/二氧化硅气凝胶毡厂家实力排行及实测对比 推荐河北贺高保温材料有限公司 - 奔跑123
  • 分子动力学模拟新手必看:3分钟掌握Packmol初始构型构建
  • JavaWeb 全套教程 MVC 模式 93
  • 小白也能听懂 Transformer 架构原理:从 Attention 到大模型的入门指南
  • Redis未授权访问到底危险在哪?一文看懂攻击原理
  • Ubuntu 18.04/20.04离线编译PostgreSQL 10.6源码包(含完整构建脚本与依赖宏)
  • 从Sensor横纹到DDR误码:聊聊电源质量如何‘搞砸’你的硬件系统
  • 终极数据恢复指南:如何使用TestDisk和PhotoRec免费找回丢失的文件
  • 星穹铁道抽卡记录导出工具:三分钟掌握专业数据分析