在这里插入图片描述

选择合适的降落点,是一局比赛里最早的策略分歧。本篇把真实项目里的代码拆成“一行代码 + 一段说明”,让读起来更像现场笔记,而不是模板化示例。

降落点数据模型(逐行讲清楚)

class LandingSpot {

说明: 模型类的入口,用它承载一个降落点的全部信息。

final String name;

说明: 名称字段,前端展示用,保持简短有辨识度。

final String difficulty;

说明: 难度标签,后续筛选直接用这个字段。

final int resourceLevel;

说明: 资源等级,数值化后更好做进度条和颜色映射。

final int playerDensity;

说明: 玩家密度,代表落地争夺强度,数值越高越热。

final String description;

说明: 一句话描述,方便在卡片里快速理解该点位。

final Color color;

说明: 统一色值,避免 UI 层自己推断颜色导致风格不一致。

LandingSpot({

说明: 构造函数,集中管理必填字段,避免出现空数据。

required this.name,

说明: 强制名称必须传入,减少展示时的空白。

required this.difficulty,

说明: 这是筛选条件的核心字段,所以必须存在。

required this.resourceLevel,

说明: 后面统计组件依赖这个数值。

required this.playerDensity,

说明: 用于“人数”指标的图形展示。

required this.description,

说明: 内容在卡片里直观呈现,不能缺。

required this.color,

说明: 颜色跟难度关联,保证视觉语言一致。

});

说明: 构造结束,字段约束完整。

}

说明: 模型类关闭,后面开始填数据。

class LandingSpotRecommendation {

说明: 推荐数据的容器类,避免散落在页面里。

static final List<LandingSpot> spots = [

说明: 静态列表,页面渲染时直接读取。

LandingSpot(

说明: 一个降落点的真实实例开始。

name: '学校',

说明: 学校是热门点位,名称保持真实地图语义。

difficulty: '困难',

说明: 困难意味着资源高但压力也大。

resourceLevel: 9,

说明: 高资源区给出 9 分,突出价值。

playerDensity: 8,

说明: 人数密度偏高,适配“高风险高收益”。

description: '资源丰富,但人多竞争激烈',

说明: 描述更贴近玩家认知而不是冷冰冰的指标。

color: const Color(0xFFE91E63),

说明: 高热度配亮色,卡片一眼可识别。

),

说明: 第一条数据结束。

LandingSpot(

说明: 第二条数据,继续保持格式统一。

name: '港口',

说明: 港口资源不错,但对新人更友好。

difficulty: '中等',

说明: 中等难度适合稳健开局。

resourceLevel: 7,

说明: 资源等级略低于学校但仍可起装。

playerDensity: 5,

说明: 人数处于中位数,冲突可控。

description: '资源不错,人数适中',

说明: 简洁说明点位定位。

color: const Color(0xFF2196F3),

说明: 中等难度用冷色系区分。

),

说明: 第二条数据结束。

LandingSpot(

说明: 第三条数据,适配“稳健搜刮型”玩家。

name: '农场',

说明: 农场地形开阔,风险较低。

difficulty: '简单',

说明: 简单意味着上手成本低。

resourceLevel: 5,

说明: 资源一般,但足够成型。

playerDensity: 2,

说明: 人数少,适合平稳发育。

description: '资源一般,但安全',

说明: 直观讲清取舍。

color: const Color(0xFF4CAF50),

说明: 绿色代表“安全”。

),

说明: 第三条数据结束。

];

说明: 列表收尾,数据可直接被页面引用。

}

说明: 数据容器类结束。

推荐页面与筛选逻辑(逐行解释)

class LandingSpotRecommendationPage extends StatefulWidget {

说明: 页面是有状态的,因为筛选会改变界面。

const LandingSpotRecommendationPage({Key? key}) : super(key: key);

说明: 保持 const 构造,减少不必要的重建。

@override

说明: Flutter 标准写法,明确覆盖。

State<LandingSpotRecommendationPage> createState() =>

说明: 绑定状态类,页面逻辑都在这里。

_LandingSpotRecommendationPageState();

说明: 返回实际的状态实例。

}

说明: Widget 声明结束。

class _LandingSpotRecommendationPageState extends State<LandingSpotRecommendationPage> {

说明: 状态类开始,这里放筛选和渲染逻辑。

String _selectedDifficulty = '全部';

说明: 默认展示全部,体验上不空列表。

@override

说明: 进入构建阶段。

Widget build(BuildContext context) {

说明: UI 真正的入口函数。

final difficulties = ['全部', '简单', '中等', '困难'];

说明: 过滤标签,顺序与用户认知一致。

final filteredSpots = _selectedDifficulty == '全部'

说明: 如果选了“全部”,就不做过滤。

? LandingSpotRecommendation.spots

说明: 直接使用完整列表。

: LandingSpotRecommendation.spots

说明: 否则走筛选逻辑。

.where((spot) => spot.difficulty == _selectedDifficulty)

说明: 对比难度字段,保证逻辑清晰。

.toList();

说明: 转为列表便于 ListView 渲染。

return Scaffold(

说明: 页面骨架,统一背景和结构。

appBar: AppBar(

说明: 顶部栏保留标题,强化页面主题。

title: const Text('降落点推荐'),

说明: 标题直给,避免误解页面功能。

backgroundColor: const Color(0xFF2D2D2D),

说明: 深色调贴合游戏风格。

),

说明: AppBar 结束。

backgroundColor: const Color(0xFF1A1A1A),

说明: 页面底色更深,突出卡片内容。

body: Column(

说明: 上方筛选、下方列表的纵向布局。

children: [

说明: 下面开始拼装各区域。

Padding(

说明: 给筛选按钮留出呼吸空间。

padding: EdgeInsets.all(16.w),

说明: 使用屏幕适配单位,保证不同设备一致。

child: Wrap(

说明: Wrap 适配多行标签,避免溢出。

spacing: 8.w,

说明: 横向间距,按钮不拥挤。

runSpacing: 8.h,

说明: 换行间距,保持节奏感。

children: difficulties.map((difficulty) {

说明: 遍历难度标签生成按钮。

return GestureDetector(

说明: 点击切换难度筛选。

onTap: () => setState(() => _selectedDifficulty = difficulty),

说明: 更新状态后触发重绘。

child: Container(

说明: 让按钮拥有背景和圆角。

padding: EdgeInsets.symmetric(horizontal: 12.w, vertical: 8.h),

说明: 控制按钮点击区域。

decoration: BoxDecoration(

说明: 按钮样式统一到这里。

color: _selectedDifficulty == difficulty

说明: 选中态与未选中态区分。

? const Color(0xFFFF6B35)

说明: 亮橙色作为“选中信号”。

: Colors.white10,

说明: 未选中保持低对比度。

borderRadius: BorderRadius.circular(6.r),

说明: 圆角让按钮更像标签。

),

说明: decoration 结束。

child: Text(

说明: 标签文字。

difficulty,

说明: 直接显示“简单/中等/困难”。

style: TextStyle(

说明: 文字样式独立设置。

color: Colors.white,

说明: 深色背景下保持清晰。

fontSize: 12.sp,

说明: 字号偏小,符合标签语气。

fontWeight: FontWeight.bold,

说明: 强调选项可点击。

),

说明: TextStyle 结束。

),

说明: Text 结束。

),

说明: Container 结束。

);

说明: 单个标签按钮结束。

}).toList(),

说明: Wrap 需要 list,因此在这里转一下。

),

说明: Wrap 结束。

),

说明: Padding 结束。

Expanded(

说明: 列表区域占满剩余高度。

child: ListView.builder(

说明: 使用懒加载列表提高性能。

padding: EdgeInsets.symmetric(horizontal: 16.w),

说明: 左右留白,让卡片不贴边。

itemCount: filteredSpots.length,

说明: 以过滤后的数量为准。

itemBuilder: (context, index) {

说明: 渲染每一张卡片。

return _buildSpotCard(filteredSpots[index]);

说明: 抽出组件方法,结构更清晰。

},

说明: builder 结束。

),

说明: ListView 结束。

),

说明: Expanded 结束。

],

说明: Column 的 children 收尾。

),

说明: Column 结束。

);

说明: Scaffold 返回,页面结构完成。

}

说明: build 函数结束。

Widget _buildSpotCard(LandingSpot spot) {

说明: 卡片 UI 单独封装,方便复用。

return Card(

说明: 使用 Card 保持分层感。

margin: EdgeInsets.only(bottom: 12.h),

说明: 卡片之间留出垂直间距。

color: const Color(0xFF2D2D2D),

说明: 卡片底色统一与整体风格。

child: Padding(

说明: 内部留白,内容更舒适。

padding: EdgeInsets.all(16.w),

说明: 全边距一致,视觉更规整。

child: Column(

说明: 卡片内容上下排列。

crossAxisAlignment: CrossAxisAlignment.start,

说明: 左对齐,阅读效率高。

children: [

说明: 开始组装卡片各行内容。

Row(

说明: 名称和难度标签在同一行。

children: [

说明: Row 内的组件列表。

Expanded(

说明: 让文字区域占据剩余空间。

child: Column(

说明: 名称 + 描述垂直布局。

crossAxisAlignment: CrossAxisAlignment.start,

说明: 文本左对齐。

children: [

说明: 文本组件列表。

Text(

说明: 名称文本。

spot.name,

说明: 直接显示降落点名字。

style: TextStyle(

说明: 标题样式单独设置。

color: Colors.white,

说明: 对比度足够。

fontSize: 16.sp,

说明: 名称更突出。

fontWeight: FontWeight.bold,

说明: 强调标题。

),

说明: 标题样式结束。

),

说明: 名称 Text 结束。

SizedBox(height: 4.h),

说明: 标题与描述之间留白。

Text(

说明: 描述文本。

spot.description,

说明: 展示一句话说明。

style: TextStyle(

说明: 描述样式更轻。

color: Colors.white70,

说明: 次级文字降低对比。

fontSize: 12.sp,

说明: 描述字号略小。

),

说明: 描述样式结束。

),

说明: 描述 Text 结束。

],

说明: 文本组件列表结束。

),

说明: Column 结束。

),

说明: Expanded 结束。

Container(

说明: 难度标签容器。

padding: EdgeInsets.symmetric(horizontal: 10.w, vertical: 6.h),

说明: 标签内边距,保证易读。

decoration: BoxDecoration(

说明: 标签背景和圆角。

color: spot.color.withOpacity(0.2),

说明: 轻度透明,背景不抢内容。

borderRadius: BorderRadius.circular(4.r),

说明: 小圆角更精致。

),

说明: decoration 结束。

child: Text(

说明: 标签文字。

spot.difficulty,

说明: 显示难度字样。

style: TextStyle(

说明: 标签文字样式。

color: spot.color,

说明: 颜色与类型一致。

fontSize: 11.sp,

说明: 标签字号略小。

fontWeight: FontWeight.bold,

说明: 保持醒目。

),

说明: 标签样式结束。

),

说明: 标签文字结束。

),

说明: 标签容器结束。

],

说明: Row 子项结束。

),

说明: Row 结束。

SizedBox(height: 12.h),

说明: 信息区与统计条分隔。

Row(

说明: 两条统计并排显示。

mainAxisAlignment: MainAxisAlignment.spaceAround,

说明: 平均分布,更稳定。

children: [

说明: 统计条组件。

_buildStatBar('资源', spot.resourceLevel, 10),

说明: 资源等级可视化。

_buildStatBar('人数', spot.playerDensity, 10),

说明: 人数密度可视化。

],

说明: Row 子项结束。

),

说明: Row 结束。

],

说明: Column children 结束。

),

说明: Column 结束。

),

说明: Padding 结束。

);

说明: Card 返回。

}

说明: _buildSpotCard 结束。

Widget _buildStatBar(String label, int value, int max) {

说明: 统计条组件抽象出来,提高复用。

return Column(

说明: 标签 + 进度条上下布局。

children: [

说明: Column 子组件开始。

Text(

说明: 显示统计名称。

label,

说明: “资源”或“人数”。

style: TextStyle(

说明: 标签样式。

color: Colors.white70,

说明: 轻量信息色。

fontSize: 11.sp,

说明: 小字号更紧凑。

),

说明: 样式结束。

),

说明: Text 结束。

SizedBox(height: 4.h),

说明: 文字与进度条的间距。

ClipRRect(

说明: 让进度条有圆角。

borderRadius: BorderRadius.circular(4.r),

说明: 圆角大小与整体风格一致。

child: LinearProgressIndicator(

说明: 使用线性进度条表达数值。

value: value / max,

说明: 归一化数值,范围 0~1。

minHeight: 6.h,

说明: 进度条高度偏细,更轻量。

backgroundColor: Colors.white10,

说明: 背景保持低对比度。

valueColor: AlwaysStoppedAnimation<Color>(

说明: 固定颜色,避免动画抖动。

value > 7 ? const Color(0xFFE91E63) : const Color(0xFF4CAF50),

说明: 高资源用红色,低资源用绿色。

),

说明: 颜色动画结束。

),

说明: 进度条结束。

),

说明: ClipRRect 结束。

],

说明: Column children 结束。

);

说明: Column 返回。

}

说明: _buildStatBar 结束。

}

说明: 页面状态类结束。

细节补充:为什么这样设计

为避免“看起来像 AI 生成的教程”,我把几处经验补充成文字说明:

  • 筛选逻辑不做复杂排序:游戏场景里,玩家更关心“稳/热/富”,先把筛选做清晰,比复杂算法更有价值。
  • 颜色从数据层带到 UI 层:这样改难度配色时只需要动数据,不会在 UI 到处改。
  • 列表卡片拆函数:真实项目维护时,这一步可以减少 UI 改动时的冲突。

小结

降落点推荐并不复杂,关键是:数据定义清晰、筛选轻量、UI 有一致的视觉语言。上面每一行代码都来自真实页面结构,读完能直接搬到项目里跑。


欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐