别卷移动端了!用 Flutter 写网页,我是不是发现了新大陆?
别卷移动端了!用 Flutter 写网页,我是不是发现了新大陆?
个人首页:忘川2006
主题: Flutter
人物:学生
引言
家人们,谁懂啊!😩
作为一名大一的“卷王”预备役,之前为了搞定毕设的前端界面,我可是在 Vue 和 React 之间反复横跳。HTML + CSS + JS 的那一套“三座大山”虽然经典,但写起来真的很容易让人心态爆炸——尤其是那个 CSS 的 float 和 position ,调个居中能调一下午,头发都掉了不少。
直到前几天,我突发奇想:既然 Flutter 号称“一套代码多端运行”,那它能不能直接写网页?
抱着“反正也不会更糟”的心态试了一下,结果……真香! 😍
这篇文章,我就带大家看看,如何用我们熟悉的 Dart 和 Widget,去“降维打击”前端开发,写一个看起来还不错的个人主页。
一、 为什么要“叛逆”地用 Flutter 写 Web?

在开始之前,我也纠结过。毕竟写网页大家都用 Vue/React。但用了 Flutter Web 之后,我发现了几个对学生党特别友好的点:
- UI 所见即所得:再也不用跟 CSS 斗智斗勇了! Center() 就是居中, Row() 就是横向排列。用写 App 的思维写网页,逻辑极度舒适。
- 代码复用率极高:如果你已经写了一个 Flutter 的 App,想做一个配套的官网?直接把 App 的组件拿过来改改就能用,几乎零成本。
- 性能其实没那么差:以前大家都说 Flutter Web 性能拉胯,但现在到了 3.x 版本,编译成 HTML 的模式下,普通网页的流畅度已经非常接近原生前端了。
二、 实战:用 Flutter 写一个“极客风”个人主页
话不多说,直接上代码。我们要做一个带有侧边导航和卡片式布局的响应式网页。
- 核心布局逻辑
Flutter Web 的关键在于响应式布局(Responsive Layout)。在电脑上是左右分栏,在手机上是上下堆叠。我们可以用 LayoutBuilder 来实现。
dart
import ‘package:flutter/material.dart’;
void main() {
runApp(const MyPersonalPage());
}
class MyPersonalPage extends StatelessWidget {
const MyPersonalPage({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: ‘我的极客主页’,
theme: ThemeData(
primarySwatch: Colors.blue,
fontFamily: ‘Roboto’, // 稍微加点字体质感
),
home: Scaffold(
body: LayoutBuilder(
builder: (context, constraints) {
// 判断屏幕宽度:如果大于800,显示宽屏模式(侧边栏+内容)
if (constraints.maxWidth > 800) {
return Row(
children: const [
Sidebar(),
Expanded(child: MainContent()),
],
);
} else {
// 手机端:侧边栏变成抽屉或者直接展示在上面
return const MainContent();
}
},
),
),
);
}
}
- 侧边栏组件(Sidebar)
这部分就像我们写 App 的 Drawer 一样简单。
dart
class Sidebar extends StatelessWidget {
const Sidebar({super.key});
@override
Widget build(BuildContext context) {
return Container(
width: 250,
color: const Color(0xFF2D3748), // 深色背景
padding: const EdgeInsets.all(20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const CircleAvatar(
radius: 60,
backgroundImage: NetworkImage(‘https://img2.baidu.com/it/u=1115662602,3599933684&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=500’),
),
const SizedBox(height: 20),
const Text(
‘张三’,
style: TextStyle(color: Colors.white, fontSize: 24, fontWeight: FontWeight.bold),
),
const Text(
‘计算机科学与技术 | 前端/移动端开发’,
style: TextStyle(color: Colors.grey, fontSize: 14),
),
const SizedBox(height: 40),
_NavItem(title: ‘首页’, icon: Icons.home),
_NavItem(title: ‘项目’, icon: Icons.code),
_NavItem(title: ‘简历’, icon: Icons.person),
_NavItem(title: ‘联系我’, icon: Icons.email),
],
),
);
}
}
class _NavItem extends StatelessWidget {
final String title;
final IconData icon;
const _NavItem({required this.title, required this.icon});
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 10),
child: Row(
children: [
Icon(icon, color: Colors.grey, size: 20),
const SizedBox(width: 10),
Text(title, style: const TextStyle(color: Colors.grey)),
],
),
);
}
}
- 主内容区(MainContent)
这里我们用 SingleChildScrollView 来防止内容溢出,并用 Card 组件展示我们的项目经历。
dart
class MainContent extends StatelessWidget {
const MainContent({super.key});
@override
Widget build(BuildContext context) {
return SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.all(40),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
‘你好,我是一名热爱技术的大学生 👋’,
style: TextStyle(fontSize: 36, fontWeight: FontWeight.bold),
),
const SizedBox(height: 10),
const Text(
‘致力于用 Flutter 连接移动与未来。’,
style: TextStyle(fontSize: 18, color: Colors.grey),
),
const SizedBox(height: 60),
// 技能标签云
const Text('我的技能', style: TextStyle(fontSize: 24)),
const SizedBox(height: 20),
Wrap(
spacing: 15,
runSpacing: 10,
children: const [
Chip(label: Text('Flutter')),
Chip(label: Text('Dart')),
Chip(label: Text('Python')),
Chip(label: Text('Git')),
Chip(label: Text('Linux')),
],
),
const SizedBox(height: 60),
// 项目展示
const Text('我的项目', style: TextStyle(fontSize: 24)),
const SizedBox(height: 20),
_ProjectCard(
title: '校园二手交易平台',
description: '基于 Flutter 的全平台应用,支持图片上传、即时聊天和定位搜索。',
tech: ['Flutter', 'Firebase', 'GetX'],
),
_ProjectCard(
title: '个人博客系统',
description: '用 Flutter Web 写的前端,配合 Spring Boot 后端的博客系统。',
tech: ['Flutter Web', 'Java', 'MySQL'],
),
],
),
),
);
}
}
class _ProjectCard extends StatelessWidget {
final String title;
final String description;
final List tech;
const _ProjectCard({required this.title, required this.description, required this.tech});
@override
Widget build(BuildContext context) {
return Card(
elevation: 4,
margin: const EdgeInsets.symmetric(vertical: 10),
child: Padding(
padding: const EdgeInsets.all(20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(title, style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
const SizedBox(height: 10),
Text(description, style: const TextStyle(color: Colors.grey)),
const SizedBox(height: 15),
Row(
children: tech.map((t) => Padding(
padding: const EdgeInsets.only(right: 8.0),
child: Text(t, style: const TextStyle(color: Colors.blue)),
)).toList(),
),
],
),
),
);
}
}
三、 怎么发布?(关键步骤)
写好了代码,怎么让别人在浏览器里看到呢?
- 编译:
在终端运行命令:
bash
flutter build web
这会在 build/web 目录下生成一堆 HTML, JS 和 CSS 文件。
2. 部署:
- 方案 A(免费):把 web 文件夹里的内容拖到 GitHub Pages 或者 Netlify Drop 上,秒级部署。
- 方案 B(自己的服务器):如果你有阿里云/腾讯云的学生机,直接把文件扔到 Nginx 的 html 目录下,配置一下路由重写(这一步很重要,否则刷新会 404),就能通过你的域名访问了。
四、
结语
说实话,用 Flutter 写 Web 对我来说是一次“开眼界”的体验。它让我意识到,技术是为了解决问题服务的,而不是为了被技术栈绑架。
虽然它可能不如 Vue/React 生态那么庞大,但对于想快速出活、或者想复用移动端代码的大学生来说,Flutter Web 绝对是一个被低估的神器。
下次做课程设计或者个人展示时,不妨试试用 Flutter 写个网页?相信我,当你发现用几行 Row 和 Column 就搞定了复杂的网页排版时,你会回来感谢我的!😎
更多推荐

所有评论(0)