Flutter for OpenHarmony Web开发助手App实战:HTTP状态码
HTTP 状态码是Web 开发绕不开的基础信息,但在真实项目里你很少会“背”它——更多时候是:调接口、看日志、对着 404/502 发呆,然后想快速查一下“它到底代表什么、该怎么处理”。这一节我们做一个404forbidden下面我按“项目里更常见的拆法”来写:数据结构、数据源、列表项、页面组合。每段代码都不长,方便你直接搬进工程里改。
HTTP 状态码是
Web 开发绕不开的基础信息,但在真实项目里你很少会“背”它——更多时候是:调接口、看日志、对着 404/502 发呆,然后想快速查一下“它到底代表什么、该怎么处理”。
这一节我们做一个 HTTP 状态码速查页,放到 Web 开发助手 App 里:
- 能搜索:输入
404或forbidden都能过滤 - 能区分等级:2xx/3xx/4xx/5xx 用颜色快速定位
- 能一键复制:点一下把状态码复制到剪贴板,方便写 issue 或排查记录
下面我按“项目里更常见的拆法”来写:数据结构、数据源、列表项、页面组合。每段代码都不长,方便你直接搬进工程里改。
1)先把状态码建模(别用 Map 乱飞)
import 'package:flutter/material.dart';
enum HttpStatusGroup {
success,
redirect,
clientError,
serverError,
}
class HttpStatusItem {
const HttpStatusItem({
required this.code,
required this.name,
required this.desc,
required this.group,
});
final int code;
final String name;
final String desc;
final HttpStatusGroup group;
Color get color {
switch (group) {
case HttpStatusGroup.success:
return Colors.green;
case HttpStatusGroup.redirect:
return Colors.blue;
case HttpStatusGroup.clientError:
return Colors.orange;
case HttpStatusGroup.serverError:
return Colors.red;
}
}
}
这里有意不用 Map<String, dynamic>:
- [更稳] 字段是强类型,
code就是int,不会出现'200'和200混着用 - [更好扩展] 以后你想加“常见处理建议”“是否可重试”等字段,不用全局搜 key
- [颜色规则集中]
color和group绑定,页面层不需要再写一堆 if/else
2)准备一份“可维护”的数据源
const httpStatusItems = <HttpStatusItem>[
HttpStatusItem(code: 200, name: 'OK', desc: '请求成功', group: HttpStatusGroup.success),
HttpStatusItem(code: 201, name: 'Created', desc: '资源已创建', group: HttpStatusGroup.success),
HttpStatusItem(code: 204, name: 'No Content', desc: '无内容', group: HttpStatusGroup.success),
HttpStatusItem(code: 301, name: 'Moved Permanently', desc: '永久重定向', group: HttpStatusGroup.redirect),
HttpStatusItem(code: 302, name: 'Found', desc: '临时重定向', group: HttpStatusGroup.redirect),
HttpStatusItem(code: 304, name: 'Not Modified', desc: '未修改', group: HttpStatusGroup.redirect),
HttpStatusItem(code: 400, name: 'Bad Request', desc: '错误的请求', group: HttpStatusGroup.clientError),
HttpStatusItem(code: 401, name: 'Unauthorized', desc: '未授权', group: HttpStatusGroup.clientError),
HttpStatusItem(code: 403, name: 'Forbidden', desc: '禁止访问', group: HttpStatusGroup.clientError),
HttpStatusItem(code: 404, name: 'Not Found', desc: '未找到', group: HttpStatusGroup.clientError),
HttpStatusItem(code: 500, name: 'Internal Server Error', desc: '服务器错误', group: HttpStatusGroup.serverError),
HttpStatusItem(code: 502, name: 'Bad Gateway', desc: '网关错误', group: HttpStatusGroup.serverError),
HttpStatusItem(code: 503, name: 'Service Unavailable', desc: '服务不可用', group: HttpStatusGroup.serverError),
];
这份数据放哪更合适?我一般会放到 lib/features/http_status/data/http_status_items.dart 这类位置:
- [集中管理] UI 页只负责展示和交互,不负责“知识库内容”
- [便于复用] 之后你做“网络错误提示组件”,也可以直接引用同一份列表
3)列表项做成独立组件,并加一个“点一下复制”
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
class HttpStatusTile extends StatelessWidget {
const HttpStatusTile({super.key, required this.item});
final HttpStatusItem item;
Widget build(BuildContext context) {
return Card(
margin: EdgeInsets.only(bottom: 12.h),
child: ListTile(
onTap: () async {
await Clipboard.setData(ClipboardData(text: item.code.toString()));
if (!context.mounted) return;
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('已复制:${item.code}')),
);
},
leading: Container(
width: 50.w,
height: 50.w,
decoration: BoxDecoration(
color: item.color.withOpacity(0.2),
borderRadius: BorderRadius.circular(8.r),
),
alignment: Alignment.center,
child: Text(
'${item.code}',
style: TextStyle(fontSize: 16.sp, fontWeight: FontWeight.bold, color: item.color),
),
),
title: Text(item.name, style: const TextStyle(fontWeight: FontWeight.bold)),
subtitle: Text(item.desc),
),
);
}
}
这个组件里我做了两件很“工程向”的小事:
- [交互更顺手] 点列表项就复制状态码,比长按选中再复制省事
- [不怕异步回调]
context.mounted这句是为了避免页面已退出时还弹SnackBar(偶发但烦)
4)页面层:加搜索框 + 过滤逻辑
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
class HttpStatusPage extends StatefulWidget {
const HttpStatusPage({super.key});
State<HttpStatusPage> createState() => _HttpStatusPageState();
}
class _HttpStatusPageState extends State<HttpStatusPage> {
final _controller = TextEditingController();
void dispose() {
_controller.dispose();
super.dispose();
}
Widget build(BuildContext context) {
final keyword = _controller.text.trim().toLowerCase();
final filtered = httpStatusItems.where((e) {
if (keyword.isEmpty) return true;
return e.code.toString().contains(keyword) ||
e.name.toLowerCase().contains(keyword) ||
e.desc.toLowerCase().contains(keyword);
}).toList();
return Scaffold(
appBar: AppBar(title: const Text('HTTP状态码')),
body: Padding(
padding: EdgeInsets.all(16.w),
child: Column(
children: [
TextField(
controller: _controller,
decoration: const InputDecoration(
hintText: '搜索:如 404 / forbidden / 网关',
prefixIcon: Icon(Icons.search),
),
onChanged: (_) => setState(() {}),
),
SizedBox(height: 12.h),
Expanded(
child: ListView.builder(
itemCount: filtered.length,
itemBuilder: (context, index) {
return HttpStatusTile(item: filtered[index]);
},
),
),
],
),
),
);
}
}
搜索逻辑我写得比较“笨但好维护”:
- [先满足常用场景] 输入数字查 code,输入英文查
name,输入中文查desc - [少引入依赖] 不上复杂搜索库,够用就行(后面真要做全文索引再升级)
- [避免大 rebuild] 这里只是小页面,
setState足够;如果列表数据很大再考虑ValueListenableBuilder或状态管理
5)别忘了 ScreenUtil 初始化(不然 .w/.h 不生效)
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
class App extends StatelessWidget {
const App({super.key});
Widget build(BuildContext context) {
return ScreenUtilInit(
designSize: const Size(360, 690),
builder: (_, __) => const MaterialApp(
home: HttpStatusPage(),
),
);
}
}
如果你项目里已经有 ScreenUtilInit,这里就不用重复包一层了。我把它单独拎出来是因为不少同学第一次抄代码会漏掉初始化,然后发现 16.w 怎么和 16 一样——其实是没初始化。
最后再补一句使用上的经验:状态码本身只是“结果”,真正定位问题时更关键的是请求链路(客户端参数/网关/后端服务/依赖服务)。这个页面更多是一个快速入口,能让你在排查时少打断一次思路。
欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net
更多推荐



所有评论(0)