代碼案例:CSS 屬性對照

0 阅读3分钟

CSS 屬性對照

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

涵蓋 10 個模塊:

模塊CSSFlutter
盒模型width/height/padding/margin/border/border-radiusContainer + BoxDecoration
文字樣式font-size/color/font-weight/letter-spacing/line-height/text-decorationTextStyle
Flex 佈局display:flex/flex-direction/justify-content/align-items/flex-wrap/gap/flex:1Row/Column/Expanded/Wrap
定位position:relative/absolute/fixed/top/left/z-indexStack + Positioned
尺寸約束width:100%/max-width/min-width/fit-contentSizedBox/ConstrainedBox/IntrinsicWidth
變換transform:rotate/scale/translateTransform.rotate/scale/translate
顯示隱藏display:none/visibility:hidden/opacity:0Visibility/Opacity + 三元表達式
陰影box-shadow/text-shadowBoxShadow/Shadow
漸變linear-gradient/radial-gradientLinearGradient/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'),
      ],
    );
  }
}