实现在Flutter Web中嵌入codemirror
博文的Flutter版本
Flutter 1.17.0 • channel beta • https://github.com/flutter/flutter.git Framework • revision d3ed9ec945 (6 天前) • 2020-04-06 14:07:34 -0700 Engine • revision c9506cb8e9 Tools • Dart 2.8.0 (build 2.8.0-dev.18.0 eea9717938)
CodeMirror 是一款“Online Source Editor”,基于Javascript,短小精悍,实时在线代码高亮显示,他不是某个富文本编辑器的附属产品,他是许多大名鼎鼎的在线代码编辑器的基础库。
codemirror
目前只有Google团队推出的dart web
项目,但是未在flutter web
中实现
我们知道,在flutter_web
中使用原生html
插件,需要使用HtmlElementView
组件支持,并在initState
中注册组件并配置组件…以下是痛苦的踩坑之路
1. 声明全局变量于Stateful Widget中
code-mirror
的html IDDivElement
,用于存放CodeMirror
,处理回调HtmlElementView
,UIoptions
,codemirror
的配置
1
2
3
4
5
6
// UI变量
String _codemirrorId = "code-edit";
html.DivElement _codeContent = html.DivElement();
CodeMirror _codeMirror;
HtmlElementView _codeView;
Map options = {'mode': 'clike', 'theme': 'monokai'};
2. initState
中注册并生成HtmlElementView
注意1:这里的
ui
是dart:ui
import 'dart:ui' as ui;
注意2:
// ignore: undefined_prefixed_name
用于忽略registerViewFactory
未定义问题
1
2
3
4
5
6
7
8
9
10
11
@override
void initState() {
super.initState();
// 注册codemirror标签
// ignore: undefined_prefixed_name
ui.platformViewRegistry
.registerViewFactory(_codemirrorId, (int viewId) => _codeContent);
_codeView = HtmlElementView(
viewType: _codemirrorId,
);
}
3. build
方法中放入_codeView
注意:一定要设置
Container
的宽高,否则出现白屏
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
body: Container(
alignment: Alignment.center,
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
FlatButton(onPressed: showCodeEditor, child: Text("show")),
Container(
decoration: BoxDecoration(color: Colors.grey),
width: 400,
height: 400,
>>>>>>>>>> child: _codeView, <<<<<<<<<<<<<<<<<<<<<<<<<<<<
),
],
),
),
4. 在_codeView
中导入CodeMirror
注意:_codeView
在对应的html中,使用了shadowRoot
进行隔离,在index.html
的header
中添加CSS、JS是无效的,无法作用在shadowRoot
内部,所以我们要另辟蹊径!
目前有两种方案:
-
使用
iFrame element
导入一个完整的html
页面,然后通过访问iFrame
的contentDocument
变量的querySelector
方法拿到html body
里我们定义好的一个空div
,然后使用codemirror.fromElement
方法填充进去❎不行,因为
Dart SDK 2.8.0
还没有将contentDocument
开放出来…GitHub Issue链接给出的解决方案是转化为JsObject
,但是这样就没法使用codemirror
的构造函数进行构造了,而且JsObject
生涩难用,成本实在太高 -
直接在
shadowRoot
中配置!(以下操作为该步骤)
4.1 拿到_codeView的shadowRoot
注意!_codeView的shadowRoot
没法通过codeView.shadowRoot
方法(无此方法)!只能通过DOM数查找,所有的HtmlView
在DOM树种都是flt-platform-view,开发时如果有多个,这个下标注意更换为对应数字
1
var node = html.document.getElementsByTagName("flt-platform-view")[0] as html.HtmlElement;
4.2 在DivElement
中实例化codemirror
1
_codeMirror = CodeMirror.fromElement(_codeContent, options: options);
4.3 导入要使用的第三方CSS、JS
这里使用
cdn.bootcss.com
的CSS和JS文件
注意innerHTML
和OuterHtml
的区别,我们要使用的是setInnerHtml
方法。注意:需要传入一个支持tag
和attributes
的验证器,否则header无法添加
1
2
3
4
5
6
7
var headElement = html.HeadElement();
// node 验证器
final NodeValidatorBuilder _htmlValidator=new NodeValidatorBuilder.common()
..allowElement('link',attributes: ["rel","href"])
..allowElement('script',attributes: ["src"]);
// 设置header
headElement.setInnerHtml('<link href="https://cdn.bootcss.com/codemirror/5.52.2/codemirror.min.css" rel="stylesheet"><script src="https://cdn.bootcss.com/codemirror/5.52.2/codemirror.min.js"></script><script src="https://cdn.bootcss.com/codemirror/5.52.2/mode/clike/clike.min.js"></script><script src="https://cdn.bootcss.com/codemirror/5.52.2/addon/selection/active-line.min.js"></script><link href="https://cdn.bootcss.com/codemirror/5.52.2/theme/monokai.min.css" rel="stylesheet">',validator: _htmlValidator);
4.4 将Header添加进shadowRoot
的children
内
1
node.shadowRoot.children.insert(0, headElement);
至此,如果配置正确,将成功实现codemirror
嵌入flutter_web
了,效果如下
但是仍然有些问题!空格
和Tab
按键会和Flutter
有些许冲突,这个之后日益完善吧~