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

在这里插入图片描述

前言

在 Web 开发中,CSS 是定义样式的标准。但在 Flutter 中,我们通过 Widget 树和 BoxDecoration 等对象来定义样式。
有时候,我们需要让 Flutter 应用支持动态样式配置,甚至是直接渲染带有 CSS 的 HTML 内容:

  • CMS 系统下发的文章带有 <style> 标签。
  • 需要实现一个支持换肤的 DSL (Domain Specific Language)。
  • 从旧的 Web 项目迁移样式逻辑。

csslib 是 Dart 官方维护的 CSS 解析器。它能将 CSS 字符串解析为 AST (抽象语法树),让你能够遍历、分析、提取其中的样式规则,并将其映射到 Flutter 的样式对象上。

对于 OpenHarmony 应用,如果你需要实现高度动态化的 UI(如 Server-Driven UI),集成 csslib 能让你的样式表达能力指数级提升。

一、核心功能

csslib 的目标不是渲染,而是 解析
它支持 CSS3 的大部分语法,包括选择器、属性、@规则等。

  1. Parse: parse(cssString) -> StyleSheet
  2. Visit: 提供了 Visitor 模式,方便遍历样式树。
  3. Selectors: 解析复杂的 CSS 选择器(如 .class > div[attr=val])。

parse()

RuleSet

Selector

Declaration

Visitor

body { color: red; }

StyleSheet (AST)

List

SelectorGroup: body

Declaration: color=red

转换为 TextStyle

二、集成与用法详解

2.1 添加依赖

dependencies:
  csslib: ^1.0.2

2.2 基础用法:解析 CSS

import 'package:csslib/parser.dart' show parse;
import 'package:csslib/visitor.dart';

void main() {
  var css = '''
    .container {
      background-color: #ffffff;
      margin: 10px;
    }
    h1 {
      font-size: 24px;
      color: blue;
    }
  ''';

  // 1. 解析
  var stylesheet = parse(css);

  // 2. 遍历规则
  for (var rule in stylesheet.topLevels) {
    if (rule is RuleSet) {
      // 打印选择器
      var selector = rule.selectorGroup!.selectors.first;
      print('Select: ${selector.simpleSelectorSequences.first.simpleSelector.name}');

      // 打印属性
      for (var decl in rule.declarationGroup.declarations) {
        if (decl is Declaration) {
          print('  Property: ${decl.property} = ${decl.expression}');
        }
      }
    }
  }
}

在这里插入图片描述

2.3 进阶用法:CSS 到 Flutter TextStyle 的转换

这是最实用的场景。我们需要写一个 Converter。

import 'package:flutter/material.dart';
import 'package:csslib/parser.dart' show parse;
import 'package:csslib/visitor.dart';

class CssToFlutterConverter {
  TextStyle convert(String cssRule) {
    var stylesheet = parse(cssRule);
    var style = TextStyle();

    // 简化版逻辑:只处理第一条规则
    var rule = stylesheet.topLevels.first as RuleSet;
    for (var decl in rule.declarationGroup.declarations) {
      if (decl is Declaration) {
        var prop = decl.property;
        var val = (decl.expression as LiteralTerm).text; // 简化:假设是字面量

        if (prop == 'color') {
          style = style.copyWith(color: _parseColor(val));
        } else if (prop == 'font-size') {
          style = style.copyWith(fontSize: double.tryParse(val.replaceAll('px', '')));
        }
      }
    }
    return style;
  }

  Color _parseColor(String name) {
    if (name == 'red') return Colors.red;
    if (name == 'blue') return Colors.blue;
    // ... 更多颜色解析
    return Colors.black;
  }
}

在这里插入图片描述

三、OpenHarmony 适配与实战:动态换肤系统

在鸿蒙应用中,我们可能需要支持用户自定义主题,或者从服务器下发主题包。如果使用 JSON 配置样式,层级太深且不直观;如果用 CSS,前端开发人员会非常熟悉。

3.1 架构设计

  1. 配置下发:服务端下发 theme.css
  2. 解析缓存:App 启动时用 csslib 解析 CSS,生成 Dart 的 ThemeData 或自定义样式对象,并缓存。
  3. 应用主题:通过 ProviderInheritedWidget 将样式分发到各个组件。

3.2 实现细节

// theme.css
// .button { background-color: #007DFF; border-radius: 8px; }

class AppTheme {
  final Color buttonColor;
  final double buttonRadius;
  
  AppTheme({required this.buttonColor, required this.buttonRadius});

  factory AppTheme.fromCss(String css) {
    // 使用 csslib 解析并提取值
    // ...
    return AppTheme(...);
  }
}

注意:CSS 的单位(px, em, rem)在 Flutter 中需要进行转换。通常 Flutter 的逻辑像素 (dp) 可以直接对应 CSS 的 px。

四、高级进阶:预处理器支持

csslib 还支持 Sass/SCSS 的部分特性(如嵌套规则、变量),尽管它的主要目标是标准 CSS。

如果你的 CSS 包含 @include$variablecsslib 可能会尝试解析或者报错(取决于具体语法)。对于复杂的预处理需求,建议在服务端编译成标准 CSS 后再下发给客户端。

五、总结

csslib 是 Dart 生态中唯一的全功能 CSS 解析器。

对于 OpenHarmony 开发者:

  • Web 迁移:如果你正在将一个 Web App 迁移到 Flutter/OpenHarmony,且想保留原有的 CSS 样式逻辑,它是必不可少的工具。
  • 动态化:它是实现基于 CSS 的动态 UI 渲染引擎的基础组件。

最佳实践

  1. 子集支持:不要试图支持浏览器能支持的所有 CSS 属性(如复杂的 Grid 布局如果不配合专门的 Flutter 库很难一一映射)。只支持你需要的那部分子集(如字体、颜色、边距)。
  2. 错误处理:CSS 语法错误很常见,解析时务必 try-catch,并提供默认样式回退。
  3. 性能:解析 CSS 是 CPU 密集型操作,大段 CSS 解析应放在 Isolate 中。

六、完整实战示例

import 'package:csslib/parser.dart' as css;
import 'package:csslib/visitor.dart';
import 'package:flutter/material.dart';

// 一个极简的 CSS 到 TextStyle 转换器
class CssStyler extends Visitor {
  Color? color;
  double? fontSize;
  FontWeight? fontWeight;

  
  void visitDeclaration(css.Declaration node) {
    // 简单处理: 解析 color, font-size, font-weight
    final prop = node.property;
    final value = node.expression!.span!.text;

    switch (prop) {
      case 'color':
        if (value == 'red') color = Colors.red;
        if (value == 'blue') color = Colors.blue;
        // Hex 解析需要额外逻辑
        break;
      case 'font-size':
        // 简单去掉 px 后缀转 double
        fontSize = double.tryParse(value.replaceAll('px', ''));
        break;
      case 'font-weight':
        if (value == 'bold') fontWeight = FontWeight.bold;
        break;
    }
  }
}

TextStyle parseToTextStyle(String cssString) {
  if (cssString.isEmpty) return TextStyle();
  try {
    // 解析 CSS 字符串
    final stylesheet = css.parse('* { $cssString }');
    final visitor = CssStyler();
    stylesheet.visit(visitor);
    
    return TextStyle(
      color: visitor.color,
      fontSize: visitor.fontSize,
      fontWeight: visitor.fontWeight,
    );
  } catch (e) {
    print('CSS Parse Error: $e');
    return TextStyle();
  }
}

void main() {
  // 模拟从后台获取的样式
  const serverStyle = 'color: red; font-size: 24px; font-weight: bold;';
  
  final style = parseToTextStyle(serverStyle);
  print('生成的 TextStyle: $style'); 
  // TextStyle(color: MaterialColor(primary value: Color(0xfff44336)), size: 24.0, weight: FontWeight.w700)
}

在这里插入图片描述

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐