普通视图

发现新文章,点击刷新页面。
昨天以前首页

代碼案例:CSS 屬性對照

2026年6月4日 16:47

CSS 屬性對照

內容: CSS 常用屬性與 Flutter Widget 屬性的逐一對照,每個部分都是可直接運行的 Widget demo。

涵蓋 10 個模塊:

模塊 CSS Flutter
盒模型 width/height/padding/margin/border/border-radius Container + BoxDecoration
文字樣式 font-size/color/font-weight/letter-spacing/line-height/text-decoration TextStyle
Flex 佈局 display:flex/flex-direction/justify-content/align-items/flex-wrap/gap/flex:1 Row/Column/Expanded/Wrap
定位 position:relative/absolute/fixed/top/left/z-index Stack + Positioned
尺寸約束 width:100%/max-width/min-width/fit-content SizedBox/ConstrainedBox/IntrinsicWidth
變換 transform:rotate/scale/translate Transform.rotate/scale/translate
顯示隱藏 display:none/visibility:hidden/opacity:0 Visibility/Opacity + 三元表達式
陰影 box-shadow/text-shadow BoxShadow/Shadow
漸變 linear-gradient/radial-gradient LinearGradient/RadialGradient
響應式 @media (max-width: 600px) MediaQuery.of(context).size.width

运行

在线效果可以复制到:dartpad.dev/ 查看,更推荐本地flutter build web调试学习

效果图

image.png

学习代码

import 'package:flutter/material.dart';

// ============================================================
// CSS → Flutter 完整对照手册
// ============================================================

void main() => runApp(const MyApp());

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(home: Scaffold(body: CssToFlutter()));
  }
}

class CssToFlutter extends StatelessWidget {
  const CssToFlutter({super.key});

  @override
  Widget build(BuildContext context) {
    return SingleChildScrollView(
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: const [
          _BoxModel(),
          _TextStyle(),
          _FlexLayout(),
          _PositionLayout(),
          _SizeConstraint(),
          _Transform(),
          _Visibility(),
          _Shadow(),
          _Gradient(),
          _Responsive(),
        ],
      ),
    );
  }
}

// ============================================================
// 一、盒模型
// CSS: width / height / padding / margin / border / border-radius / background
// ============================================================
class _BoxModel extends StatelessWidget {
  const _BoxModel();

  @override
  Widget build(BuildContext context) {
    return Container(
      // CSS: width: 200px
      width: 200,
      // CSS: height: 200px
      height: 200,
      // CSS: margin: 20px
      margin: const EdgeInsets.all(20),
      // CSS: padding: 16px
      padding: const EdgeInsets.all(16),
      // CSS: padding: 10px 20px         → 上下10 左右20
      // padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 20),
      // CSS: padding: 10px 20px 30px 40px → 上右下左
      // padding: const EdgeInsets.fromLTRB(40, 10, 20, 30),
      decoration: BoxDecoration(
        // CSS: background: blue
        color: Colors.blue,
        // CSS: border-radius: 20px
        borderRadius: BorderRadius.circular(20),
        // CSS: border-radius: 10px 20px 30px 40px
        // borderRadius: const BorderRadius.only(
        //   topLeft: Radius.circular(10),
        //   topRight: Radius.circular(20),
        //   bottomRight: Radius.circular(30),
        //   bottomLeft: Radius.circular(40),
        // ),
        // CSS: border: 3px solid yellow
        border: Border.all(width: 3, color: Colors.yellow),
        // CSS: border-top: 3px solid red  → 单边边框
        // border: const Border(top: BorderSide(width: 3, color: Colors.red)),
      ),
      child: const Text('盒模型'),
    );
  }
}

// ============================================================
// 二、文字样式
// CSS: font-size / color / font-weight / font-style / letter-spacing /
//      line-height / text-align / text-decoration / overflow
// ============================================================
class _TextStyle extends StatelessWidget {
  const _TextStyle();

  @override
  Widget build(BuildContext context) {
    return Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        Text(
          '文字样式示例',
          style: const TextStyle(
            // CSS: font-size: 24px
            fontSize: 24,
            // CSS: color: red
            color: Colors.red,
            // CSS: font-weight: bold
            fontWeight: FontWeight.bold,
            // CSS: font-weight: 300
            // fontWeight: FontWeight.w300,
            // CSS: font-style: italic
            fontStyle: FontStyle.italic,
            // CSS: letter-spacing: 2px
            letterSpacing: 2,
            // CSS: line-height: 1.5
            height: 1.5,
            // CSS: text-decoration: underline
            decoration: TextDecoration.underline,
            // CSS: text-decoration: line-through
            // decoration: TextDecoration.lineThrough,
          ),
          // CSS: text-align: center
          textAlign: TextAlign.center,
          // CSS: overflow: hidden; white-space: nowrap; text-overflow: ellipsis
          overflow: TextOverflow.ellipsis,
          maxLines: 1,
        ),

        // CSS: text-transform: uppercase → Flutter 无内置,用 .toUpperCase()
        Text('hello'.toUpperCase()),
      ],
    );
  }
}

// ============================================================
// 三、Flex 布局
// CSS: display:flex / flex-direction / justify-content /
//      align-items / flex-wrap / gap / flex:1
// ============================================================
class _FlexLayout extends StatelessWidget {
  const _FlexLayout();

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        // CSS: display:flex; flex-direction: row; justify-content: space-between; align-items: center
        Row(
          mainAxisAlignment: MainAxisAlignment.spaceBetween, // justify-content
          crossAxisAlignment: CrossAxisAlignment.center, // align-items
          children: const [
            Text('左'),
            Text('中'),
            Text('右'),
          ],
        ),

        // CSS: display:flex; flex-direction: column; align-items: center
        Column(
          crossAxisAlignment: CrossAxisAlignment.center,
          children: const [
            Text('上'),
            Text('中'),
            Text('下'),
          ],
        ),

        // CSS: flex: 1(占满剩余空间)
        Row(
          children: [
            Expanded(
                child: Container(color: Colors.red, height: 40)), // flex: 1
            Expanded(
                child: Container(color: Colors.blue, height: 40)), // flex: 1
          ],
        ),

        // CSS: flex: 2 / flex: 1(按比例)
        Row(
          children: [
            Expanded(
                flex: 2,
                child: Container(color: Colors.green, height: 40)), // flex: 2
            Expanded(
                flex: 1,
                child: Container(color: Colors.orange, height: 40)), // flex: 1
          ],
        ),

        // CSS: flex-wrap: wrap; gap: 8px
        Wrap(
          spacing: 8, // CSS: column-gap
          runSpacing: 8, // CSS: row-gap
          children: List.generate(
            6,
            (i) => Container(
              width: 80,
              height: 40,
              color: Colors.purple,
              child: Center(child: Text('item $i')),
            ),
          ),
        ),

        // justify-content 对照表:
        // flex-start       → MainAxisAlignment.start
        // flex-end         → MainAxisAlignment.end
        // center           → MainAxisAlignment.center
        // space-between    → MainAxisAlignment.spaceBetween
        // space-around     → MainAxisAlignment.spaceAround
        // space-evenly     → MainAxisAlignment.spaceEvenly

        // align-items 对照表:
        // flex-start       → CrossAxisAlignment.start
        // flex-end         → CrossAxisAlignment.end
        // center           → CrossAxisAlignment.center
        // stretch          → CrossAxisAlignment.stretch
        // baseline         → CrossAxisAlignment.baseline
      ],
    );
  }
}

// ============================================================
// 四、定位
// CSS: position: relative/absolute/fixed、top/left/right/bottom、z-index
// ============================================================
class _PositionLayout extends StatelessWidget {
  const _PositionLayout();

  @override
  Widget build(BuildContext context) {
    return SizedBox(
      width: 200,
      height: 200,
      // CSS: position: relative(父容器)
      child: Stack(
        children: [
          // 普通流中的元素
          Container(color: Colors.grey),

          // CSS: position: absolute; top: 10px; left: 10px; z-index: 1
          const Positioned(
            top: 10,
            left: 10,
            child: Text('左上角'),
          ),

          // CSS: position: absolute; bottom: 0; right: 0
          const Positioned(
            bottom: 0,
            right: 0,
            child: Text('右下角'),
          ),

          // CSS: position: absolute; top:50%; left:50%; transform: translate(-50%,-50%)
          const Positioned.fill(
            child: Align(
              alignment: Alignment.center,
              child: Text('居中'),
            ),
          ),

          // Stack 里越靠后的 child z-index 越高,对应 CSS z-index
        ],
      ),
    );
  }
}

// ============================================================
// 五、尺寸与约束
// CSS: width / height / max-width / min-width / max-height / min-height / box-sizing
// ============================================================
class _SizeConstraint extends StatelessWidget {
  const _SizeConstraint();

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        // CSS: width: 200px; height: 50px
        const SizedBox(width: 200, height: 50),

        // CSS: width: 100%(撑满父级宽度)
        const SizedBox(width: double.infinity, height: 50),

        // CSS: max-width: 300px; min-width: 100px
        ConstrainedBox(
          constraints: const BoxConstraints(
            minWidth: 100,
            maxWidth: 300,
            minHeight: 40,
            maxHeight: 100,
          ),
          child: Container(color: Colors.teal),
        ),

        // CSS: width: fit-content(包裹内容)
        IntrinsicWidth(
          child:
              Container(color: Colors.amber, child: const Text('fit-content')),
        ),
      ],
    );
  }
}

// ============================================================
// 六、变换
// CSS: transform: rotate / scale / translate
// ============================================================
class _Transform extends StatelessWidget {
  const _Transform();

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        // CSS: transform: rotate(0.05rad)
        Transform.rotate(
          angle: 0.05,
          child: Container(width: 100, height: 50, color: Colors.blue),
        ),

        // CSS: transform: scale(1.5)
        Transform.scale(
          scale: 1.5,
          child: Container(width: 100, height: 50, color: Colors.green),
        ),

        // CSS: transform: translate(20px, 10px)
        Transform.translate(
          offset: const Offset(20, 10),
          child: Container(width: 100, height: 50, color: Colors.red),
        ),
      ],
    );
  }
}

// ============================================================
// 七、显示与隐藏
// CSS: display: none / visibility: hidden / opacity: 0
// ============================================================
class _Visibility extends StatelessWidget {
  const _Visibility();

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        // CSS: display: none(不占位,从树中移除)
        Visibility(
          visible: false,
          child: Container(width: 100, height: 50, color: Colors.red),
        ),

        // CSS: visibility: hidden(占位,但不可见)
        Visibility(
          visible: false,
          maintainSize: true,
          maintainAnimation: true,
          maintainState: true,
          child: Container(width: 100, height: 50, color: Colors.red),
        ),

        // CSS: opacity: 0.5
        Opacity(
          opacity: 0.5,
          child: Container(width: 100, height: 50, color: Colors.blue),
        ),

        // 简单显隐:三元表达式(display:none 等价)
        // condition ? MyWidget() : const SizedBox.shrink(),
      ],
    );
  }
}

// ============================================================
// 八、阴影
// CSS: box-shadow / text-shadow
// ============================================================
class _Shadow extends StatelessWidget {
  const _Shadow();

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        // CSS: box-shadow: 4px 4px 8px rgba(0,0,0,0.3)
        Container(
          width: 100,
          height: 100,
          decoration: BoxDecoration(
            color: Colors.white,
            boxShadow: const [
              BoxShadow(
                color: Color(0x4D000000), // rgba(0,0,0,0.3)
                offset: Offset(4, 4), // x, y
                blurRadius: 8, // blur
                spreadRadius: 0, // spread
              ),
            ],
          ),
        ),

        // CSS: text-shadow: 2px 2px 4px rgba(0,0,0,0.5)
        const Text(
          '文字阴影',
          style: TextStyle(
            shadows: [
              Shadow(
                color: Color(0x80000000),
                offset: Offset(2, 2),
                blurRadius: 4,
              ),
            ],
          ),
        ),
      ],
    );
  }
}

// ============================================================
// 九、渐变
// CSS: background: linear-gradient / radial-gradient
// ============================================================
class _Gradient extends StatelessWidget {
  const _Gradient();

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        // CSS: background: linear-gradient(to right, red, blue)
        Container(
          width: 200,
          height: 60,
          decoration: const BoxDecoration(
            gradient: LinearGradient(
              begin: Alignment.centerLeft, // to left
              end: Alignment.centerRight, // to right
              colors: [Colors.red, Colors.blue],
            ),
          ),
        ),

        // CSS: background: linear-gradient(135deg, red, blue)
        Container(
          width: 200,
          height: 60,
          decoration: const BoxDecoration(
            gradient: LinearGradient(
              begin: Alignment.topLeft,
              end: Alignment.bottomRight,
              colors: [Colors.red, Colors.blue],
            ),
          ),
        ),

        // CSS: background: radial-gradient(circle, red, blue)
        Container(
          width: 200,
          height: 60,
          decoration: const BoxDecoration(
            gradient: RadialGradient(
              colors: [Colors.red, Colors.blue],
            ),
          ),
        ),
      ],
    );
  }
}

// ============================================================
// 十、响应式
// CSS: @media (max-width: 600px)
// ============================================================
class _Responsive extends StatelessWidget {
  const _Responsive();

  @override
  Widget build(BuildContext context) {
    // CSS: @media 等价于读取屏幕尺寸
    final screenWidth = MediaQuery.of(context).size.width;
    final screenHeight = MediaQuery.of(context).size.height;
    final isSmall = screenWidth < 600;

    return Column(
      children: [
        // CSS: @media (max-width: 600px) { font-size: 14px } else { font-size: 20px }
        Text(
          '响应式文字',
          style: TextStyle(fontSize: isSmall ? 14 : 20),
        ),

        // CSS: @media (max-width: 600px) { flex-direction: column }
        isSmall
            ? const Column(children: [Text('A'), Text('B')])
            : const Row(children: [Text('A'), Text('B')]),

        Text('屏幕宽: $screenWidth, 高: $screenHeight'),
      ],
    );
  }
}


pub.dev 常用包 vs npm 生态对照

2026年6月4日 15:27

pub.dev 常用包 vs npm 生态对照

Flutter 的 pub.dev 等价于 npm,pubspec.yaml 等价于 package.json


一、包管理命令对照

npm / yarn Flutter 说明
npm install flutter pub get 安装所有依赖
npm install axios flutter pub add dio 添加单个包
npm uninstall axios flutter pub remove dio 移除包
npm update flutter pub upgrade 升级所有包
npm outdated flutter pub outdated 查看过期包
package.json pubspec.yaml 依赖配置文件
package-lock.json pubspec.lock 锁定版本文件
node_modules/ .pub-cache/ 本地缓存目录
npm run build flutter build apk / ios / web 构建
npm start flutter run 开发运行
npx dart run 执行 Dart 脚本

二、网络请求

npm 包 pub.dev 包 说明
axios dio HTTP 客户端,拦截器、取消请求、上传进度
fetch(原生) http 官方轻量 HTTP 包
socket.io-client web_socket_channel WebSocket
graphql graphql_flutter GraphQL 客户端
dependencies:
  dio: ^5.4.0
  http: ^1.2.0
  web_socket_channel: ^2.4.0

三、状态管理

npm 包 pub.dev 包 说明
React Context(内置) provider 官方推荐,基于 InheritedWidget
zustand riverpod 现代状态管理,类型安全
redux flutter_bloc Redux 思想,Bloc 模式
mobx mobx + flutter_mobx 响应式状态管理
jotai riverpod(atom) 原子化状态
valtio get GetX,简单轻量
dependencies:
  provider: ^6.1.0         # React Context 等价
  flutter_riverpod: ^2.5.0  # Zustand 等价(推荐)
  flutter_bloc: ^8.1.0      # Redux 等价

四、路由导航

npm 包 pub.dev 包 说明
react-router-dom go_router 声明式路由(官方推荐)
react-router-dom(v5) auto_route 代码生成路由
Navigator 1.0 内置 Navigator 命令式路由(无需额外包)
dependencies:
  go_router: ^13.0.0

五、UI 组件库

npm 包 pub.dev 包 说明
antd / MUI Flutter 内置 Material 官方 Material Design
antd / MUI flutter_cupertino iOS 风格组件(内置)
第三方 UI 库 flutter_ui_kit 社区 UI 组件
图表:recharts fl_chart 折线图、柱状图、饼图
图表:echarts syncfusion_flutter_charts 功能更全的图表库
轮播:swiper carousel_slider 图片轮播
瀑布流:自定义 flutter_staggered_grid_view 瀑布流/不等高网格
下拉刷新 pull_to_refresh 下拉刷新 + 上拉加载
骨架屏 shimmer Loading 骨架屏效果
dependencies:
  fl_chart: ^0.66.0
  carousel_slider: ^4.2.1
  flutter_staggered_grid_view: ^0.7.0
  shimmer: ^3.0.0

六、本地存储

npm 包 pub.dev 包 说明
localStorage shared_preferences K-V 存储,等价于 localStorage
IndexedDB / localforage hive 轻量高性能本地数据库
sqlite sqflite SQLite 数据库
cookies flutter_secure_storage 安全存储(加密,存 Token)
redux-persist hydrated_bloc 状态持久化
dependencies:
  shared_preferences: ^2.2.0    # localStorage 等价
  flutter_secure_storage: ^9.0.0 # 安全存储 Token
  hive: ^2.2.3                   # 本地数据库
  hive_flutter: ^1.1.0
  sqflite: ^2.3.0                # SQLite

七、图片处理

npm 包 pub.dev 包 说明
react-lazyload cached_network_image 网络图片缓存 + 占位符
react-dropzone image_picker 选择图片/相机拍照
compressorjs flutter_image_compress 图片压缩
SVG(内置) flutter_svg SVG 图片支持
lottie-react lottie Lottie 动画
dependencies:
  cached_network_image: ^3.3.0
  image_picker: ^1.0.0
  flutter_svg: ^2.0.0
  lottie: ^3.0.0

八、工具类

npm 包 pub.dev 包 说明
dayjs / date-fns intl 日期格式化
uuid uuid 生成 UUID
lodash Dart 内置(collection 包) 数组/对象工具
classnames 无(直接条件判断) -
crypto-js crypto 加密(Dart 内置)
js-cookie flutter_secure_storage Cookie / Token 存储
i18next flutter_localizations + intl 国际化 i18n
react-i18next easy_localization i18n(更易用)
@sentry/react sentry_flutter 错误监控
react-query flutter_query / riverpod 服务端状态管理
dependencies:
  intl: ^0.19.0
  uuid: ^4.3.0
  easy_localization: ^3.0.0
  sentry_flutter: ^7.0.0

flutter:
  generate: true   # 开启 i18n 代码生成

九、权限与设备

场景 pub.dev 包 说明
权限请求(相机/位置/通知) permission_handler 统一权限管理
获取 GPS 位置 geolocator 地理位置
设备信息 device_info_plus 设备型号、系统版本
网络状态 connectivity_plus 检测网络连接
本地通知 flutter_local_notifications 本地推送
分享 share_plus 系统分享
打开链接 url_launcher 打开浏览器/拨号
剪贴板 内置 Clipboard 复制粘贴
dependencies:
  permission_handler: ^11.0.0
  geolocator: ^11.0.0
  connectivity_plus: ^6.0.0
  url_launcher: ^6.2.0
  share_plus: ^7.2.0

十、开发工具(devDependencies)

npm 包 pub.dev 包 说明
eslint flutter_lints 代码规范检查
prettier dart format(内置) 代码格式化
jest flutter_test(内置) 单元测试
@testing-library/react flutter_test(内置) Widget 测试
husky - Git hooks(需手动配置)
json-server json_serializable JSON 模型代码生成
typescript Dart(强类型内置) 类型系统(无需额外安装)
dev_dependencies:
  flutter_test:
    sdk: flutter
  flutter_lints: ^5.0.0
  json_serializable: ^6.7.0   # fromJson/toJson 代码自动生成
  build_runner: ^2.4.0        # 代码生成工具

十一、pubspec.yaml 完整示例

name: my_app
description: My Flutter App
version: 1.0.0+1

environment:
  sdk: ^3.0.0

dependencies:
  flutter:
    sdk: flutter

  # 网络
  dio: ^5.4.0
  cached_network_image: ^3.3.0

  # 状态管理
  provider: ^6.1.0

  # 路由
  go_router: ^13.0.0

  # 存储
  shared_preferences: ^2.2.0
  flutter_secure_storage: ^9.0.0

  # 工具
  intl: ^0.19.0
  uuid: ^4.3.0

  # UI
  fl_chart: ^0.66.0
  lottie: ^3.0.0
  flutter_svg: ^2.0.0

dev_dependencies:
  flutter_test:
    sdk: flutter
  flutter_lints: ^5.0.0
  build_runner: ^2.4.0
  json_serializable: ^6.7.0

flutter:
  uses-material-design: true
  assets:
    - assets/images/
    - assets/animations/
  fonts:
    - family: PingFang
      fonts:
        - asset: assets/fonts/PingFang.ttf

十二、版本约束规则

# 等价于 npm 的 semver
dio: ^5.4.0   # ^:兼容版本(5.x.x),等价于 npm ^
dio: ~5.4.0   # ~:补丁版本(5.4.x),等价于 npm ~
dio: 5.4.0    # 精确版本,等价于 npm 5.4.0
dio: ">=5.0.0 <6.0.0"  # 范围
dio: any      # 任意版本(不推荐)

Flutter ThemeData 主题系统

2026年6月4日 15:27

Flutter ThemeData 主题系统

等价于 CSS Variables / Tailwind 主题 / styled-components ThemeProvider


一、核心对照

CSS / React Flutter 说明
--primary-color: #6200EE colorScheme.primary 主色
--background: #FFFFFF colorScheme.surface 背景色
--text-color: #000000 colorScheme.onSurface 文字色
--error-color: #B00020 colorScheme.error 错误色
CSS Variables 全局继承 Theme.of(context) 读取 任何子组件可读
:root { --color: red } MaterialApp(theme: ThemeData(...)) 全局定义
var(--primary-color) Theme.of(context).colorScheme.primary 使用主题色
@media (prefers-color-scheme: dark) darkTheme: ThemeData(...) 暗黑模式
styled-components ThemeProvider MaterialApp(theme: ...) 主题注入

二、定义全局主题

MaterialApp(
  theme: ThemeData(
    // Material 3 推荐:从种子色自动生成整套配色
    // 等价于 Tailwind 的 primary 色系自动生成 50-900
    colorScheme: ColorScheme.fromSeed(
      seedColor: const Color(0xFF6200EE),   // 主色种子
      brightness: Brightness.light,
    ),
    useMaterial3: true,

    // 也可以手动指定每个颜色角色
    // colorScheme: const ColorScheme.light(
    //   primary: Color(0xFF6200EE),        // 主色
    //   secondary: Color(0xFF03DAC6),      // 次要色
    //   surface: Color(0xFFFFFFFF),        // 背景
    //   error: Color(0xFFB00020),          // 错误色
    //   onPrimary: Colors.white,           // 主色上的文字
    //   onSurface: Color(0xFF000000),      // 背景上的文字
    // ),
  ),

  // 暗黑模式主题
  darkTheme: ThemeData(
    colorScheme: ColorScheme.fromSeed(
      seedColor: const Color(0xFF6200EE),
      brightness: Brightness.dark,         // 切换为深色
    ),
    useMaterial3: true,
  ),

  // 跟随系统 / 强制亮色 / 强制暗色
  themeMode: ThemeMode.system,   // ThemeMode.light / ThemeMode.dark
)

三、ColorScheme 颜色角色

Material 3 设计系统,每个颜色都有对应的「在其上的文字色」

颜色角色 用途 等价 CSS Variable
primary 主操作、重要按钮 --color-primary
onPrimary primary 上的文字/图标 --color-primary-text
secondary 次要操作 --color-secondary
surface 卡片、底部栏背景 --color-surface
onSurface surface 上的文字 --color-on-surface
background 页面背景(M3 已废弃,用 surface) --color-background
error 错误提示 --color-error
onError error 上的文字 --color-error-text
inversePrimary AppBar 上的背景色 -
outline 边框、分隔线 --color-border
// 在任意子组件中读取主题色
// 等价于 CSS: color: var(--color-primary)
final colorScheme = Theme.of(context).colorScheme;

Container(color: colorScheme.primary)
Text('标题', style: TextStyle(color: colorScheme.onSurface))

四、TextTheme 文字样式系统

等价于 Tailwind 的 text-xs / text-sm / text-base / text-lg ...

// 全局定义
ThemeData(
  textTheme: const TextTheme(
    displayLarge:   TextStyle(fontSize: 57, fontWeight: FontWeight.w400),
    displayMedium:  TextStyle(fontSize: 45),
    displaySmall:   TextStyle(fontSize: 36),
    headlineLarge:  TextStyle(fontSize: 32, fontWeight: FontWeight.bold),
    headlineMedium: TextStyle(fontSize: 28),
    headlineSmall:  TextStyle(fontSize: 24),
    titleLarge:     TextStyle(fontSize: 22, fontWeight: FontWeight.w500),
    titleMedium:    TextStyle(fontSize: 16),
    titleSmall:     TextStyle(fontSize: 14),
    bodyLarge:      TextStyle(fontSize: 16),
    bodyMedium:     TextStyle(fontSize: 14),  // 默认正文
    bodySmall:      TextStyle(fontSize: 12),
    labelLarge:     TextStyle(fontSize: 14, fontWeight: FontWeight.bold),  // 按钮文字
    labelMedium:    TextStyle(fontSize: 12),
    labelSmall:     TextStyle(fontSize: 11),
  ),
)

// 使用
Text('标题', style: Theme.of(context).textTheme.headlineMedium)
Text('正文', style: Theme.of(context).textTheme.bodyMedium)

// 等价于:
// <h2 className="text-2xl font-bold">标题</h2>
// <p className="text-sm">正文</p>

五、组件主题定制

等价于 CSS 组件样式覆盖 / styled-components

ThemeData(
  // 按钮全局样式(等价于 .btn { border-radius: 8px })
  elevatedButtonTheme: ElevatedButtonThemeData(
    style: ElevatedButton.styleFrom(
      shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
      padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 12),
    ),
  ),

  // 输入框全局样式
  inputDecorationTheme: const InputDecorationTheme(
    border: OutlineInputBorder(),
    contentPadding: EdgeInsets.symmetric(horizontal: 16, vertical: 12),
  ),

  // AppBar 全局样式
  appBarTheme: const AppBarTheme(
    elevation: 0,
    centerTitle: true,
  ),

  // Card 全局样式
  cardTheme: CardTheme(
    elevation: 2,
    shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
  ),

  // 全局字体
  fontFamily: 'Roboto',
)

六、局部覆盖主题

// 局部覆盖(等价于 CSS scope / styled-components 局部样式)
Theme(
  data: Theme.of(context).copyWith(
    colorScheme: Theme.of(context).colorScheme.copyWith(
      primary: Colors.red,   // 只改这个子树的主色
    ),
  ),
  child: ElevatedButton(
    onPressed: () {},
    child: const Text('红色按钮'),
  ),
)

七、暗黑模式适配

// 读取当前亮/暗模式
final isDark = Theme.of(context).brightness == Brightness.dark;

// 根据模式选色(等价于 CSS prefers-color-scheme)
Container(
  color: isDark ? Colors.grey[900] : Colors.white,
)

// 推荐:直接用 colorScheme,自动适配亮暗
// colorScheme.surface 在亮色主题是白色,暗色主题是深灰
Container(
  color: Theme.of(context).colorScheme.surface,
)

八、自定义颜色扩展

// 扩展 ColorScheme,添加自定义颜色(等价于扩展 CSS Variables)
@immutable
class AppColors extends ThemeExtension<AppColors> {
  final Color success;
  final Color warning;

  const AppColors({required this.success, required this.warning});

  @override
  AppColors copyWith({Color? success, Color? warning}) => AppColors(
        success: success ?? this.success,
        warning: warning ?? this.warning,
      );

  @override
  AppColors lerp(AppColors other, double t) => AppColors(
        success: Color.lerp(success, other.success, t)!,
        warning: Color.lerp(warning, other.warning, t)!,
      );
}

// 注册
ThemeData(
  extensions: const [
    AppColors(success: Color(0xFF4CAF50), warning: Color(0xFFFFC107)),
  ],
)

// 使用
final appColors = Theme.of(context).extension<AppColors>()!;
Icon(Icons.check, color: appColors.success)

九、完整主题配置示例

ThemeData get lightTheme => ThemeData(
  useMaterial3: true,
  colorScheme: ColorScheme.fromSeed(seedColor: const Color(0xFF6200EE)),
  fontFamily: 'PingFang SC',
  textTheme: const TextTheme(
    titleLarge: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
    bodyMedium: TextStyle(fontSize: 14, height: 1.5),
  ),
  elevatedButtonTheme: ElevatedButtonThemeData(
    style: ElevatedButton.styleFrom(
      shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
    ),
  ),
  appBarTheme: const AppBarTheme(elevation: 0, centerTitle: true),
  cardTheme: CardTheme(
    shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
  ),
);

ThemeData get darkTheme => lightTheme.copyWith(
  colorScheme: ColorScheme.fromSeed(
    seedColor: const Color(0xFF6200EE),
    brightness: Brightness.dark,
  ),
);

十、主题速查

Theme.of(context).colorScheme.primaryvar(--color-primary)
Theme.of(context).colorScheme.surfacevar(--color-background)
Theme.of(context).colorScheme.errorvar(--color-error)
Theme.of(context).textTheme.headlineMedium.text-2xl.font-bold
Theme.of(context).textTheme.bodyMedium.text-sm(默认正文)
Theme.of(context).brightness                  → prefers-color-scheme

React Hooks → Flutter 等价写法

2026年6月4日 15:25

React Hooks → Flutter 等价写法

适合 React 开发者快速上手 Flutter,直接从你熟悉的 Hooks 映射


总览对照表

React Hook Flutter 等价 说明
useState State 字段 + setState 响应式状态
useEffect(() => {}, []) initState 挂载时执行一次
useEffect(() => {}, [dep]) didUpdateWidget 依赖变化时执行
useEffect(() => { return () => {} }) dispose 卸载时清理
useRef State 字段(不调用 setState) 不触发重建的引用
useMemo const Widget / 手动缓存字段 缓存计算结果
useCallback State 类方法(天然稳定) 缓存函数引用
useContext InheritedWidget / Provider.of 跨层级读取数据
useReducer Bloc / 手写 reducer 复杂状态管理
useLayoutEffect addPostFrameCallback 渲染后同步执行
forwardRef GlobalKey 父组件访问子组件
React.memo const Widget 避免不必要重建
lazy + Suspense FutureBuilder 异步加载
createPortal Overlay 渲染到根层

一、useState → State 字段 + setState

// React
const [count, setCount] = useState(0);
const [name, setName] = useState('');
const [user, setUser] = useState(null);

setCount(c => c + 1);  // 函数式更新
// Flutter
class _MyState extends State<MyWidget> {
  int count = 0;
  String name = '';
  User? user;          // null 用 ? 表示可空

  void increment() {
    setState(() {
      count += 1;      // 直接修改,无需函数式更新
    });
  }
}

差异:Flutter 直接修改字段值,不需要 prev => prev + 1 这种函数式更新。 setState 的作用只是通知框架重建,不负责计算新值。


二、useEffect → initState / didUpdateWidget / dispose

2.1 挂载时执行一次(依赖数组为空 []

// React
useEffect(() => {
  fetchData();
  startTimer();
}, []);
// Flutter
@override
void initState() {
  super.initState();
  fetchData();
  startTimer();
}

2.2 依赖变化时执行(依赖数组有值 [dep]

// React
useEffect(() => {
  fetchUser(userId);
}, [userId]);
// Flutter
@override
void didUpdateWidget(covariant MyWidget oldWidget) {
  super.didUpdateWidget(oldWidget);
  // 对比新旧 widget 属性,手动判断是否变化
  if (oldWidget.userId != widget.userId) {
    fetchUser(widget.userId);
  }
}

2.3 卸载时清理(return 清理函数)

// React
useEffect(() => {
  final sub = stream.listen(handler);
  return () => sub.cancel();  // 清理
}, []);
// Flutter
StreamSubscription? _sub;

@override
void initState() {
  super.initState();
  _sub = stream.listen(handler);
}

@override
void dispose() {
  _sub?.cancel();  // 清理
  super.dispose();
}

2.4 每次 render 后执行(无依赖数组)

// React
useEffect(() => {
  console.log('每次渲染后执行');
});
// Flutter —— 不推荐,但可以在 build 末尾用 addPostFrameCallback
@override
Widget build(BuildContext context) {
  WidgetsBinding.instance.addPostFrameCallback((_) {
    // 每次 build 完成后执行
  });
  return ...;
}

三、useRef → 普通字段(不触发重建)

// React
const timerRef = useRef(null);
const inputRef = useRef(null);

timerRef.current = setTimeout(...);
inputRef.current.focus();
// Flutter
// 普通字段就是 useRef,修改它不触发 setState 就不会重建
Timer? _timer;
final FocusNode _focusNode = FocusNode();

// 访问 Widget 实例(等价于 inputRef.current)
final GlobalKey _key = GlobalKey();

规律:React 需要 useRef 是因为函数组件每次 render 都会重新声明变量。 Flutter 的 State 类只创建一次,普通字段天然就是"ref"。


四、useMemo → const Widget / 缓存字段

// React
const expensiveValue = useMemo(() => {
  return heavyCalculation(data);
}, [data]);

const element = useMemo(() => <ExpensiveComponent />, []);
// Flutter

// 缓存计算结果:手动在 initState 或 didUpdateWidget 里计算并存字段
List<Item> _sorted = [];

@override
void initState() {
  super.initState();
  _sorted = [...widget.items]..sort();  // 只算一次
}

// 缓存 Widget:用 const,编译期复用,父 build 重建不影响它
const ExpensiveWidget()   // ✅ 等价于 useMemo(() => <Component />, [])

五、useCallback → State 类方法(天然稳定)

// React —— 每次 render 函数引用会变,需要 useCallback 稳定引用
const handleClick = useCallback(() => {
  setCount(c => c + 1);
}, []);
// Flutter —— 方法定义在 State 类里,引用天然稳定,不需要任何处理
void _handleClick() {
  setState(() => count++);
}

// 使用
TextButton(onPressed: _handleClick, child: Text('点击'))

差异:Flutter 完全不存在这个问题,State 类方法不会在每次 build 时重新创建。


六、useContext → Provider.of / context.read

// React
const theme = useContext(ThemeContext);
const { user, setUser } = useContext(UserContext);
// Flutter(以 Provider 包为例,对应 React Context)

// 读取并订阅(数据变化时重建,等价于 useContext)
final theme = context.watch<ThemeModel>();

// 只读取一次,不订阅(等价于 useRef 读 context,不触发重建)
final user = context.read<UserModel>();

// 调用方法
context.read<UserModel>().logout();

七、useReducer → Bloc / 手写 reducer

// React
const reducer = (state, action) => {
  switch (action.type) {
    case 'increment': return { ...state, count: state.count + 1 };
    case 'reset':     return { count: 0 };
  }
};
const [state, dispatch] = useReducer(reducer, { count: 0 });
dispatch({ type: 'increment' });
// Flutter —— 手写 reducer(轻量场景)
class CountState {
  final int count;
  const CountState(this.count);
}

CountState reducer(CountState state, String action) {
  switch (action) {
    case 'increment': return CountState(state.count + 1);
    case 'reset':     return const CountState(0);
    default:          return state;
  }
}

class _MyState extends State<MyWidget> {
  CountState _state = const CountState(0);

  void dispatch(String action) {
    setState(() => _state = reducer(_state, action));
  }
}

八、useLayoutEffect → addPostFrameCallback

// React —— DOM 更新后同步执行,获取元素尺寸
useLayoutEffect(() => {
  const rect = ref.current.getBoundingClientRect();
  setHeight(rect.height);
}, []);
// Flutter —— 帧渲染完成后执行,获取 Widget 尺寸
@override
void initState() {
  super.initState();
  WidgetsBinding.instance.addPostFrameCallback((_) {
    final box = _key.currentContext?.findRenderObject() as RenderBox?;
    final size = box?.size;
    setState(() => _height = size?.height ?? 0);
  });
}

九、React.memo → const Widget

// React —— 父组件重渲染时,props 没变就跳过
const Child = React.memo(({ title }) => <div>{title}</div>);
// Flutter —— 方式1:const(最简单,适合静态内容)
const MyWidget()   // 编译期确定,父 build 重建时直接复用

// Flutter —— 方式2:不依赖外部变化的子 Widget 天然不重建
// Flutter 的 diff 算法:类型相同 + key 相同 = 复用 State,不重新创建

十、整体对比:组件生命周期

React 函数组件                    Flutter StatefulWidget
─────────────────────────────────────────────────────────
渲染阶段:
  function Component()         ←→   Widget build(context)

挂载:
  useEffect(() => {}, [])      ←→   initState()

依赖更新:
  useEffect(() => {}, [dep])   ←→   didUpdateWidget()

Context 变化:
  useContext 自动订阅           ←→   didChangeDependencies()

卸载:
  useEffect(() => () => {})    ←→   dispose()

每帧渲染后:
  useLayoutEffect              ←→   addPostFrameCallback

十一、常见陷阱对比

场景 React 陷阱 Flutter 等价陷阱
闭包捕获旧值 useEffect 依赖数组漏写 didUpdateWidget 忘记判断新旧值
内存泄漏 忘记清理订阅 dispose 里忘记 cancel/dispose
死循环 useEffectsetState 未加条件 build 里直接调用 setState
异步 setState 组件卸载后 setState dispose 后调用 setState(会报错)
不必要重建 未用 memo / useCallback 未用 const / RepaintBoundary

CSS → Flutter 对照手册

2026年6月4日 15:24

CSS → Flutter 对照手册

核心思维转变:CSS 是给元素加属性,Flutter 是用 Widget 嵌套实现布局


一、盒模型

CSS Flutter 说明
width / height Container(width, height) / SizedBox 固定尺寸
max-width / min-width ConstrainedBox(constraints) 尺寸约束
padding: 16px EdgeInsets.all(16) 四边相同
padding: 10px 20px EdgeInsets.symmetric(vertical:10, horizontal:20) 上下/左右
padding: 10px 20px 30px 40px EdgeInsets.fromLTRB(40, 10, 20, 30) 上右下左
margin Container(margin: ...) 外边距
background: blue Container(color: Colors.blue) 纯色背景
border-radius: 20px BorderRadius.circular(20) 圆角
border-radius: 10px 20px 30px 40px BorderRadius.only(topLeft: ..., ...) 单独圆角
border: 3px solid yellow Border.all(width:3, color:Colors.yellow) 边框
border-top: 3px solid red Border(top: BorderSide(width:3, color:Colors.red)) 单边边框
box-shadow BoxDecoration(boxShadow: [BoxShadow(...)]) 阴影
overflow: hidden ClipRRect / ClipOval 裁剪溢出
width: fit-content IntrinsicWidth 包裹内容宽度
width: 100% SizedBox(width: double.infinity) / Expanded 撑满父级

⚠️ Containercolordecoration 互斥,用了 decoration 就把颜色写进 BoxDecoration(color:...)


二、Flex 布局

CSS Flutter 说明
display: flex; flex-direction: row Row 横向排列
display: flex; flex-direction: column Column 纵向排列
justify-content mainAxisAlignment 主轴对齐
align-items crossAxisAlignment 交叉轴对齐
flex: 1 Expanded 占满剩余空间
flex: 2 (比例) Expanded(flex: 2) 按比例分配
flex-grow 不强制填满 Flexible 弹性但不强制
flex-wrap: wrap Wrap 自动换行
gap / column-gap Wrap(spacing: 8) 横向间距
row-gap Wrap(runSpacing: 8) 纵向间距
align-self Align 包裹单个子项 单独控制对齐

justify-content 对照

CSS Flutter
flex-start MainAxisAlignment.start
flex-end MainAxisAlignment.end
center MainAxisAlignment.center
space-between MainAxisAlignment.spaceBetween
space-around MainAxisAlignment.spaceAround
space-evenly MainAxisAlignment.spaceEvenly

align-items 对照

CSS Flutter
flex-start CrossAxisAlignment.start
flex-end CrossAxisAlignment.end
center CrossAxisAlignment.center
stretch CrossAxisAlignment.stretch
baseline CrossAxisAlignment.baseline

三、定位

CSS Flutter 说明
position: relative(父容器) Stack 绝对定位容器
position: absolute; top/left/right/bottom Positioned(top, left, right, bottom) 绝对定位子项
position: absolute; top:50%; transform:translate(-50%,-50%) Positioned.fill + Align(Alignment.center) 绝对居中
z-index Stack 中越靠后的 child 层级越高 层叠顺序
position: sticky SliverPersistentHeader(pinned: true) 滚动置顶
position: fixed ScaffoldfloatingActionButton / bottomNavigationBar 固定在屏幕

四、文字样式

CSS Flutter 说明
font-size: 20px TextStyle(fontSize: 20) 字号
color: red TextStyle(color: Colors.red) 颜色
font-weight: bold TextStyle(fontWeight: FontWeight.bold) 粗体
font-weight: 300 TextStyle(fontWeight: FontWeight.w300) 字重
font-style: italic TextStyle(fontStyle: FontStyle.italic) 斜体
letter-spacing: 2px TextStyle(letterSpacing: 2) 字间距
line-height: 1.5 TextStyle(height: 1.5) 行高
text-decoration: underline TextStyle(decoration: TextDecoration.underline) 下划线
text-decoration: line-through TextStyle(decoration: TextDecoration.lineThrough) 删除线
text-align: center Text(textAlign: TextAlign.center) 对齐
overflow: ellipsis Text(overflow: TextOverflow.ellipsis, maxLines: 1) 超出省略
text-transform: uppercase text.toUpperCase() 大写
text-shadow TextStyle(shadows: [Shadow(...)]) 文字阴影

五、背景与装饰

CSS Flutter 说明
background: blue BoxDecoration(color: Colors.blue) 纯色背景
background: linear-gradient(to right, red, blue) LinearGradient(begin: Alignment.centerLeft, end: Alignment.centerRight, colors: [...]) 线性渐变
background: linear-gradient(135deg, red, blue) LinearGradient(begin: Alignment.topLeft, end: Alignment.bottomRight, colors: [...]) 对角渐变
background: radial-gradient(circle, red, blue) RadialGradient(colors: [...]) 径向渐变
background-image: url(...) DecorationImage(image: AssetImage(...)) 背景图
background-size: cover DecorationImage(fit: BoxFit.cover) 背景图填充

六、变换

CSS Flutter 说明
transform: rotate(0.05rad) Transform.rotate(angle: 0.05) 旋转(弧度)
transform: rotate(45deg) Transform.rotate(angle: 45 * pi / 180) 旋转(角度转弧度)
transform: scale(1.5) Transform.scale(scale: 1.5) 缩放
transform: translate(20px, 10px) Transform.translate(offset: Offset(20, 10)) 平移
transform-origin Transform(alignment: Alignment.topLeft, ...) 变换原点

七、显示与隐藏

CSS Flutter 说明
display: none(不占位) Visibility(visible: false) / 三元返回 SizedBox.shrink() 移除占位
visibility: hidden(占位) Visibility(visible: false, maintainSize: true, maintainState: true, maintainAnimation: true) 保留占位
opacity: 0.5 Opacity(opacity: 0.5) 透明度
pointer-events: none IgnorePointer / AbsorbPointer 禁用触摸

八、滚动

CSS Flutter 说明
overflow: scroll(单个子项) SingleChildScrollView 单子滚动
overflow: scroll(列表) ListView 列表滚动
overflow-x: scroll SingleChildScrollView(scrollDirection: Axis.horizontal) 横向滚动
scroll-snap PageView 分页滑动
虚拟列表 ListView.builder 懒加载长列表
网格滚动 GridView 网格列表
混合滚动 CustomScrollView + Sliver* 复杂滚动

九、响应式

CSS Flutter 说明
@media (max-width: 600px) MediaQuery.of(context).size.width < 600 屏幕宽度判断
vw / vh MediaQuery.of(context).size.width/height 视口尺寸
grid-template-columns: repeat(auto-fill, minmax(150px,1fr)) LayoutBuilder + 动态 crossAxisCount 自适应列数
父级约束响应 LayoutBuilder(builder: (context, constraints) {...}) 获取父级约束

十、经典布局速查

布局名 CSS 方案 Flutter 方案
圣杯布局 float / flex Column + Row + Expanded
双飞翼布局 margin Row + Expanded(主内容优先)
粘性底部 min-height: 100vh + margin-top: auto Column + Expanded
水平垂直居中 flex + justify/align: center Center / Align
瀑布流 column-count / JS 计算 双列 Column 交叉填充
底部导航 固定定位 BottomNavigationBar + IndexedStack
侧边抽屉 transform: translateX Drawer
折叠头部 position: sticky + JS SliverAppBar
主从布局 @media 切换 MediaQuery + 条件渲染
固定头尾中间滚动 flex-column + overflow: auto Column + Expanded + ListView

十一、最大的思维差异

CSS 思维 Flutter 思维
一个元素加多个属性 多个 Widget 嵌套组合
样式可以全局继承 样式靠 ThemeData 统一管理
父级影响子级布局 父 Widget 向子 Widget 传递约束(constraints)
z-index 控制层级 Stack 中越靠后的 child 层级越高
媒体查询响应式 MediaQuery.of(context).size 获取屏幕尺寸
display: none 隐藏元素 三元表达式直接移除 Widget
全局 class 复用样式 封装成独立 Widget 复用
CSS 变量 var(--color) Theme.of(context).colorScheme.primary
伪元素 ::before / ::after Stack + Positioned 叠加 Widget
transition 过渡动画 AnimatedContainer / AnimatedOpacity 等 Animated* 系列

十二、Widget 选择决策树

需要样式装饰(背景/圆角/边框/阴影)?
  └─ 是 → BoxDecoration(放在 Container 或 DecoratedBox 里)

需要布局子元素?
  ├─ 横向 → Row
  ├─ 纵向 → Column
  ├─ 叠加 → Stack
  └─ 网格 → GridView

需要滚动?
  ├─ 单个内容 → SingleChildScrollView
  ├─ 列表 → ListView.builder
  └─ 复杂滚动(折叠头/置顶) → CustomScrollView + Sliver*

只需固定尺寸,不需要装饰?
  └─ SizedBox(比 Container 更轻量)

只需内边距?
  └─ Padding(比 Container 更语义化)

只需对齐?
  └─ Align / Center
❌
❌