Clojrue+Dart的使用手册
- 命令行界面 (CLI)
- 使用 Dart 包
- 类型和别名
- 类型和可空性
- 参数化类型
- 属性访问
- 对象解构:
:flds - 构造函数
- 理解 Dart 的函数签名
- 命名参数
- 可选参数(命名或非命名)
- 枚举
- 常量表达式
- 创建类
- 操作符
- Getters/Setters
- 必须的库
f/run [& widget-body]f/widget [& widget-body]- .child-threading
:let指令:key指令:watch指令- 可监视对象(
watchables) - Cells
:managed指令:bind指令:get指令:context指令:vsync指令- 参考连接

这是一份<ClojureDart 对 Clojurists 的备忘单>的全文翻译.
本文涵盖了使用 ClojureDart 进行 Dart 编程的多个方面, 包括命令行工具, Dart包的使用, 类型和可空性, 属性访问, 构造函数, Dart的函数签名, 枚举, 类的创建和 Flutter 的使用等.
命令行界面 (CLI)
项目的管理命令.
clj -M:cljd init: 初始化项目clj -M:cljd flutter: 项目启动, hotload由按回车(return)触发clj -M:cljd compile: 编译clj -M:cljd clean: 清理, 当出现问题时使用clj -M:cljd upgrade: 保持 CLJD 更新
使用 Dart 包
在 ns 表单中, 像往常一样使用 :require, 但用字符串代替符号.
示例: (ns my.project (:require ["package:flutter/material.dart" :as m]))
类型和别名
不同于 Clojure/JVM, 可以使用别名前缀类型. 例如, m/ElevatedButton 指的是别名为m的包中的ElevatedButton.
如果是JVM, 只能是使用全名了:
(new com.example.package1.MyClass)
(new com.example.package2.MyClass)
类型和可空性
- 在 ClojureDart 中,
^String x表示 x 不能为 nil. - 使用
^String? x来允许 nil 值.
参数化类型
与 Clojure/JVM 不同, 泛型也存在于运行时, 除了单类型提示比如^String又增加了一种新的范型提示. ^#/(List Map) x, 用来表示List<Map> x.
| Dart | ClojureDart |
|---|---|
List | List |
List<Map> | #/(List Map) |
List?<Map> | #/(List? Map) |
List<Map?> | #/(List Map?) |
属性访问
- 实例获取:
(.-prop obj), 赋值:(.-prop! obj x)或(set! (.-prop obj) x) - 静态属性:
m/Colors.purple, 颜色比较特殊, 可以取色阶m/Colors.purple.shade900, 也可以在色阶上设置透明度(m/Colors.purple.shade900.withAlpha 128).
对象解构: :flds
新引入的对象解构方法, 类似于对一个map进行解构的时候, 可以使用 :keys, :strs 和 :syms.
对一个Dart对象, 我们可以使用:flds进行解构.
示例: (let [{:flds [height width]} size] ...) 等同于解构 height 和 width 属性.
(let [height (.-height size)
width (.-width size)]
...)
构造函数
- 在Dart中, 构造函数不总是分配新实例, 有时会返回现有实例. 因此, ClojureDart 中没有
new关键字或末尾的点(String. ...)这种语法. - 默认构造函数用类名直接调用, 如
(StringBuffer "hello"). - 命名构造函数类似于静态方法调用, 如
(List/empty .growable true).
理解 Dart 的函数签名
writeAll(Iterable objects, [String separator = ""])表示一个类型为Iterable的位置参数:objects和一个可选的位置参数``separator类型为String, 默认值为"".RegExp(String source, {bool multiLine = false, bool caseSensitive = true})表示一个类型为String的位置参数:source和两个可选的命名布尔参数``multiLine(默认为false)和caseSensitive(默认为true).any(bool test(E element)),any作为一个函数, 接受函数test为参数,test函数返回bool, 而且接受一个类型为E的位置参数:element.
命名参数
一些 Dart 函数和方法需要命名参数, 在 ClojureDart 中使用 .argname.
示例: (m/Text "Hello world" .maxLines 2 .softWrap true .overflow m/TextOverflow.fade)
可选参数(命名或非命名)
实现 Dart 可选参数的函数或方法时的语法.
[a b c .d .e]表示 3 个位置参数和 2 个命名参数:d和e.[a b c ... d e]5 个位置参数, 3个固定2个可选.[.a 42 .e]两个命名参数a和b,a有默认值42.[... a 42 b]2 个可选的位置参数a和b,b的默认值是42.
枚举
枚举值是类型上的静态属性. 例如, m/TextAlign.left.
常量表达式
- Dart会在编译时进行常量去重(Constant Deduplication)和常量折叠(Constant Folding), 特别是使用常量构造函数的时候.
- ClojureDart 最大程度地推断常量表达式. 在某些罕见情况下(如创建哨兵或令牌), 必须将表达式标记为唯一:
^:unique (Object),否则, 总是会得到相同的实例.
创建类
reify,deftype和defrecord可以用于类型的创建:extends用于扩展类, 甚至是抽象类.- Mixins 使用
^:mixin标记.
操作符
Dart 支持操作符重载, 但不是所有操作符都是有效的 Clojure symbol(比如[]=).
所以, 可以在ClojureDart 中使用字符串作为方法名
list[i] = 42对应(. list "[]=" i 42), 设置list中i对应的值为42.map[k] = v对应(. map "[]=" k v)设置map中k对应的v.list[i]对应(. list "[]" i)获取list中下标为i的元素.map[k]改写为(. map "[]" k)获取map中keyk对应的value.
Getters/Setters
- Dart 的属性
properties不全是字段fields, 大多数是getters/setter. 它们的行为类似于字段, 可以通过(.-prop obj)获取和(set! (.-prop obj) 42)或(.-prop! obj 42)设置. getters和setters是定义在reify/deftype/defrecord中的普通函数,getter的需要一个参数[this], 而setter需要俩[this v].- 如果定义在上级class或者接口中, 我们直接使用就好, 但是如果是自己定义的函数, 则需要增加
^:getteror^:setter.
必须的库
cljd.flutter 不是一个框架, 而是一个实用程序库, 旨在减少样板代码, 使 Flutter 更适合 Clojurists 的口味.
必须引入的两个库
[cljd.flutter :as f]["package:flutter/material.dart" :as m].
f/run [& widget-body]
- 从
main函数调用, 启动应用程序(一个 Widget). 其主体按照f/widget解释执行. 使应用程序的根部分可重载.
f/widget [& widget-body]
- 所有宏的妈. 这个macro求值为一
个Widget. 其主体由交错的表达式(epxression)和指令(directive)组成. 指令总是以关键字开头, 后跟一个表达式.指令范围从日常的:let到特定的:vsync.- 表达式通过命名参数
.childthreading macro展开.
.child-threading
- 在 "widget-body"中, 表达式通过名为
.child的命名参数进行threading macro展开.(f/widget m/Center (m/Text "hello"))等价于
(m/Center .child (m/Text "hello"))当两个表达式由带点的符号分隔时, 使用该符号进行threading.
m/MaterialApp .home m/Scaffold .body (m/Text "Don't stop it now!" )
:let 指令
(f/widget :let [some bindings] ...)重写为(let [some bindings] (f/widget ...)).
:key 指令
- 用于识别列表中的兄弟小部件, 避免状态混乱.
:watch 指令
与:let不同, 表达式必须是可监视的, 绑定形式将依次绑定到由其可监视对象产生的值.
:watch [v an-atom]
(m/Text (str v))
当an-atom改变时, :watch之后的所有内容将使用v绑定到an-atom的当前值进行更新.
此外, :watch支持:as操作符, 以暴露watchable本身.
:watch [mode (atom m/ThemeMode.system)
:as mode-atom]
;; 后面可以使用`swap!` `reset!`去修改 mode-atom
可监视对象(watchables)
nil,atoms,cells,Streams,Futures,Listenables和ValueListenables以及任何Subscribable协议的扩展.
Cells
Cells非常适合维护和重用派生状态; 提示: 尝试通过继承绑定(参见 :bind)而不是函数参数来共享它们.
(f/$ " cache" ) ; 创建一个 cell
(f/$ expr), 类似于电子表格单元, 每当依赖项变化时更新其值.
依赖项可以是任何可监视对象, 包括其他 cells. 使用 f/<!(" take" )读取依赖项. f/<! 可以在直接或间接由 cell 调用的任何函数中使用.
:managed 指令
用于自动管理 *Controllers 等对象的生命周期.
:managed 接受一个绑定向量, 类似于 :let, 但表达式必须生成需要被释放的对象(默认使用.dispose方法).
:bind 指令
沿着组件树而建立的动态绑定. :bind {:k v}, 建立一个从:k到v的继承绑定, 对所有子级组件可见.
所有的子组件都可以使用:get拿到这些绑定的变量.
:get 指令
- 检索通过
:bind绑定的值.:get [:k1 :k2], 通过:bind检索绑定到:k1和:k2的值. - 获取能够通过
context获得的值::get [m/Navigator], 检索由(m/Navigator.of context)返回的实例.:get [m/Theme]通过(m/Theme.of context)获取当前应用的主题ThemeData, 用于访问颜色, 字体样式等主题属性.:get [m/MediaQuery]通过(m/MediaQuery.of context)获取当前设备的媒体信息(如屏幕尺寸, 像素密度, 文本缩放因子), 用于适配不同屏幕.:get [m/Scaffold]通过(m/Scaffold.of context)获取ScaffoldState, 可以管理当前页面的Scaffold(如显示SnackBar, 打开Drawer等).:get [m/Form]通过(m/Form.of context)获取FormState, 用于在表单中进行验证, 保存或重置操作.:get [m/Localizations]通过(m/Localizations.of context, AppLocalizations)获取本地化资源(如文本, 格式化信息等), 适用于支持多语言的应用.:get [m/DefaultTabController]通过(m/DefaultTabController.of context)获取TabController, 用于管理TabBar和TabView的控制和切换.:get [m/Overlay]通过(m/Overlay.of context)获取OverlayState, 用于在页面上动态添加或移除悬浮组件.:get [m/ModalRoute]通过(m/ModalRoute.of context)获取当前的Route, 通常用于访问页面的参数(如settings.arguments).
:context 指令
当Flutter请求更多上下文时使用, 比如:context ctx, 将ctx绑定到一个BuildContext实例.
在Flutter中, BuildContext是一个非常重要的概念. 它代表了构建Widget树时的上下文信息, 是Flutter中许多功能的关键.
BuildContext是Flutter用来在Widget树中定位和管理Widget的工具, 它包含了Widget树的层次结构信息. 以下是BuildContext的几个核心用途:
- 获取父Widget信息
- Widget树的构建和重建
- 与InheritedWidget通信
- 在StatelessWidget和StatefulWidget中使用
- 访问路由, 媒体查询, 主题等信息
:vsync 指令
绑定到 TickerProvider, 通常用于动画.