This commit is contained in:
xiaojia.dxj
2019-08-06 17:31:46 +08:00
78 changed files with 3251 additions and 359 deletions

26
.github/ISSUE_TEMPLATE/bug-report.md vendored Normal file
View File

@ -0,0 +1,26 @@
---
name: "\U0001F41B Bug Report"
about: Something isn't working as expected
---
## Bug Report
**仅限中文与英文**, 其他语言的提交将直接被关闭
请先确认查找了已有的 issue [GitHub issues](https://github.com/apache/incubator-shardingsphere-example/issues).
为了更好的收录您反馈或者提交的相关pr. 请您关注您提交的问题, 我们可能需要更多的详细信息, 我们会在issue下先您收集相关信息,
如果长时间未得到您的回复, 如果我们无法在某些环境上重现该问题, 并且您**超过7天未回复**, 我们可能会关 **闭掉issue**, 谢谢
### 您当前的flutter doctor信息
### 预期的表现
### 实际的表现
### 预期的分析 (给出您能想到, 任何您能想到的)
### 重现的方式, 例如从 A界面 点击 b, 跳转到B页面, 界面出现溢出乱码等.
### 用于重现此问题或者可能解决以上问题的示例代码例如github 链接代码)

View File

@ -0,0 +1,18 @@
---
name: "\U0001F680 Feature Request"
about: I have a suggestion
---
## Feature Request
**仅限中文与英文**, 其他语言的提交将直接被关闭
请先确认查找了已有的 issue [GitHub issues](https://github.com/apache/incubator-shardingsphere-example/issues).
为了更好的收录您反馈或者提交的相关pr. 请您关注您提交的问题, 我们可能需要更多的详细信息, 我们会在issue下先您收集相关信息,
如果长时间未得到您的回复, 如果我们无法在某些环境上重现该问题, 并且您**超过7天未回复**, 我们可能会关 **闭掉issue**, 谢谢
### 您的功能需求是否与哪些问题有关?
### 描述您想要的功能.

14
.github/ISSUE_TEMPLATE/question.md vendored Normal file
View File

@ -0,0 +1,14 @@
---
name: "\U0001F914 Question"
about: Usage question that isn't answered in docs or discussion
---
## Question
**仅限中文与英文**, 其他语言的提交将直接被关闭
请先确认查找了已有的 issue [GitHub issues](https://github.com/apache/incubator-shardingsphere-example/issues).
为了更好的收录您反馈或者提交的相关pr. 请您关注您提交的问题, 我们可能需要更多的详细信息, 我们会在issue下先您收集相关信息,
如果长时间未得到您的回复, 如果我们无法在某些环境上重现该问题, 并且您**超过7天未回复**, 我们可能会关 **闭掉issue**, 谢谢

20
.github/ISSUE_TEMPLATE/widget about.md vendored Normal file
View File

@ -0,0 +1,20 @@
---
name: "📄 Widget About"
about: something about widget
---
## Widget About
**仅限中文与英文**, 其他语言的提交将直接被关闭
请先确认查找了已有的 issue [GitHub issues](https://github.com/apache/incubator-shardingsphere-example/issues).
为了更好的收录您反馈或者提交的相关pr. 请您关注您提交的问题, 我们可能需要更多的详细信息, 我们会在issue下先您收集相关信息,
如果长时间未得到您的回复, 如果我们无法在某些环境上重现该问题, 并且您**超过7天未回复**, 我们可能会关 **闭掉issue**, 谢谢
## 描述widget 类型
## widget 简要描述
##

View File

@ -85,4 +85,4 @@ The advantages of Flutter mainly include:
</tbody>
</table>
Powered by [Alibaba Auction Front-end Team](https://github.com/alibaba-paimai-frontend)<img src="https://img.alicdn.com/tfs/TB1foEhAMHqK1RjSZJnXXbNLpXa-166-166.png" width= 20 height=20>
Powered by Alibaba Auction Front-end Team<img src="https://img.alicdn.com/tfs/TB1foEhAMHqK1RjSZJnXXbNLpXa-166-166.png" width= 20 height=20>

View File

@ -1,6 +1,8 @@
Language: [English](https://github.com/alibaba/flutter-go/blob/master/README-en.md) | [中文简体](https://github.com/alibaba/flutter-go/blob/master/README.md)
## Flutter Go
# test
![https://img.alicdn.com/tfs/TB1OJkeHNYaK1RjSZFnXXa80pXa-229-229.png](https://img.alicdn.com/tfs/TB1OJkeHNYaK1RjSZFnXXa80pXa-229-229.png)
> 帮助开发者快速上手 Flutter **Flutter Go 1.0 Android版已正式发布**
@ -126,3 +128,4 @@ flutter优点主要包括
- 大家的互相信任,尊重与支持,才是开源社区前进的动力和来源.
Powered by 阿里拍卖前端团队<img src="https://img.alicdn.com/tfs/TB1foEhAMHqK1RjSZJnXXbNLpXa-166-166.png" width=20 height=20>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 360 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 673 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 215 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 192 KiB

67
docs/contribute.md Normal file
View File

@ -0,0 +1,67 @@
# Flutter Go PR 规范说明
# 贡献指南
此项目遵循[贡献者行为准则](https://github.com/spring-projects/spring-framework/blob/master/CODE_OF_CONDUCT.adoc)。参与此项目即表示您同意遵守其条款.
如何提PR请先阅读以下文档
- [如何提PR](https://github.com/alibaba/flutter-go/blob/master/docs/push-pr.md)
- [如何使用go-cli](https://github.com/alibaba/flutter-go/blob/master/docs/go-cli.md);
- [dart 代码规范](https://github.com/alibaba/flutter-go/blob/beta/Flutter_Go%20%E4%BB%A3%E7%A0%81%E5%BC%80%E5%8F%91%E8%A7%84%E8%8C%83.md)
# Issue
PR的第一步就是提交issue即提交你发现的BUG 或者 想加入的功能, 选择你issue在类型
![](https://img.alicdn.com/tfs/TB1r3LEbKL2gK0jSZFmXXc7iXXa-858-317.png)
您的 Pull Request 可能包含以下几种
- 当前项目逻辑代码的问题修复或者优化
- widget 示例的完善
- widget 文档的完善与更新
# 文档与DEMO的完善
一个widget的demo实例与文档说明, 由以下目录构成, 我们以**/lib/widgets/components/Tab/Tab**组件举例, 在此文件目录下构建您的demo运行即可看到效果
- index.dart
- demo.dart
# 撰写 Pull Request
为了更好的将 *flutter* 的各种使用方法分享给大家, 我们欢迎第三方提交个人Pull Request(简称为PR)到开源仓库中. 提交的PR需要满足以下规则:
- PR 的提交名称, 请使用有意义可以理解的词汇, 否则我们请直接关闭它.
- 例如: 增加了XX功能, 优化..., 修复在 XX 状态下对 XX 的异常处理
- PR 只能被提交合并到 *develop* 分支, 被提交到master的 PR, 我们将会直接关闭.
- PR 的描述区,需要描述本次改动的主要内容, 以及为什么要如此改动.
- 避免超大的 PR 提交
- 当 PR 的改动 **change** 超过1000行(暂定为1000)时, 请尽量拆分后进行提交.
- 规范化的commit信息
- commit规范参照[develop规范](https://github.com/alibbaba/flutter-go/blob/master/develop.md#commit-%E6%8F%90%E4%BA%A4%E8%A7%84%E8%8C%83)
- commit列表, 请在提交PR之前做好整理, 避免出现一个功能点的多条commit.[如何整理commit](https://help.github.com/en/articles/using-git-rebase-on-the-command-line)
# 如何提交PR
* fork项目到自己仓库
* git clone (您的仓库地址)到本地
* 建立上游源
* git remote add upStream git@github.com:alibaba/flutter-go.git
* 创建开发分支(非必须)
* git checkout -b develop
* 修改提交代码
* git status
* git add .
* git commit -m 'feat: message'
* git push origin develop
* 同步代码
* git fetch upstream
* git merge upstream/develop
* git push origin develop
* 提交PR
* 到自己github仓库对应fork的项目下new pull request

109
docs/go-cli.md Normal file
View File

@ -0,0 +1,109 @@
# GoCli 使用方式
## 安装
获取最新flutterGo代码分之后. 在源文件下会有 **go-cli** 的文件.
首先进入该文件夹并安装go-cli所需要的依赖
```
cd go-cli
pub get
```
然后使用pub global命令将文件包注册到全局
```
pub global activate --source path /{your flutter go absolute path}/fluttergo/go-cli
```
使用pub global list命令查看全局包列表 如果看到有 **goCli 1.0.0**则证明安装成功
```
goCli 1.0.0 at path "/{youpath}/flutter-go/go-cli"
```
## 使用方式
现在支持以下几种命令
- createDemo 新增flutter go demo.
- createPage 新增flutter go page.
- watch 动态生成文档与demo相关
### createDemo
动态生成widget demo, 可以创建demo.以便详情页中使用
在flutter go 根文件下通过命令行输入以上命令可以进行以下操作
[✓] 请输入新增加的demo名称? demoName
[✓] 请输入您的姓名(使用英文) yourName
[✓] 请输入您的github的email地址 yourEmail
[✓] 请输入您demo的描述 这是一个测试的标准demo
在完成以上操作后, 可以得到这样的输出:
```
------------------
您新增的组件信息如下
==================
{
name : demoName
author : yourName
email : yourEmail
desc : 这是一个测试的标准demo
}
==================
[✓] Is this the config you want ? (Y/n) y
{
新建的demo文件位于 : /flutter go/lib/page_demo_package/demoName_yourName_1a29aa8e_32ae_4241_9c8a_5c9e1f92b096
demoId为 : 1a29aa8e_32ae_4241_9c8a_5c9e1f92b096
markdown中调用方式 : [demo:1a29aa8e_32ae_4241_9c8a_5c9e1f92b096]
}
```
您可以在任意详情页中, 通过以下方式调用
```
[demo: 1a29aa8e_32ae_4241_9c8a_5c9e1f92b096]
```
### createPage
使用方式同上. 通过该命令可以创建标准的详情页.您可以通过修改index.md进行动态的更新您所创建的详情页.
目录结构为
```
standard_pages/
├── index.dart (不可人为修改)
└── standard_sanfan_ee4feb8e_32ae_4241_9c8a_5c9e1f92b096
├── .page.json (不可人为修改)
├── index.dart (不可人为修改)
└── index.md (可修改)
```
### watch
监听并编译standard_pages与page_demo_package下的的文件改动. 动态处理demo目录与文件markdown转化等.
## 注意
- 在修改page_demo_package或者standard_pages目录下的文件操作时. 建议在flutterGo目录执行goCLi watch 开启文件动态编译
- name, author 字段必须使用英文开头, 不允许使用特殊符号. 正常的示范 name ='name_test' author = 'abcdefg';
- 暂时阶段demo与page,一经过创建不允许修改名称作者等信息. 凡是被收录进主分支的不允许被删除

Binary file not shown.

Before

Width:  |  Height:  |  Size: 106 KiB

35
docs/push-pr.md Normal file
View File

@ -0,0 +1,35 @@
# 如何为一个项目提PR
## PR 的含义
PR的全称是 **pull request**, 可以理解成. 让开源项目拉取合并他方的请求.
## pull request
1. 将 开源flutter go项目 fork到我们自己的仓库.
![如何fock项目](https://img.alicdn.com/tfs/TB1XbnDbQH0gK0jSZPiXXavapXa-1425-672.gif)
2. 将Fork的仓库 clone 到本地,进行本地修改.
3. 将改动push到自己的仓库中
```
git add {some change file}
git commit ...
git push origin {your branch}
```
4. 登陆github, 从**自己的仓库**向**开源项目仓库** 发起 **pull request** (合并申请);
过程参考:
![如何提pr](https://img.alicdn.com/tfs/TB1zV_BbKL2gK0jSZPhXXahvXXa-1425-672.gif)
5. 开源项目维护者会review您的 **pull request**,展开讨论或者修改, 一旦通过审核,开源项目维护者合并该分支到正式仓库然后并关闭合并申请。

109
go-cli/Readme.md Normal file
View File

@ -0,0 +1,109 @@
# GoCli 使用方式
## 安装
获取最新flutterGo代码分之后. 在源文件下会有 **go-cli** 的文件.
首先进入该文件夹并安装go-cli所需要的依赖
```
cd go-cli
pub get
```
然后使用pub global命令将文件包注册到全局
```
pub global activate --source path /{your flutter go absolute path}/fluttergo/go-cli
```
使用pub global list命令查看全局包列表 如果看到有 **goCli 1.0.0**则证明安装成功
```
goCli 1.0.0 at path "/{youpath}/flutter-go/go-cli"
```
## 使用方式
现在支持以下几种命令
- createDemo 新增flutter go demo.
- createPage 新增flutter go page.
- watch 动态生成文档与demo相关
### createDemo
动态生成widget demo, 可以创建demo.以便详情页中使用
在flutter go 根文件下通过命令行输入以上命令可以进行以下操作
[✓] 请输入新增加的demo名称? demoName
[✓] 请输入您的姓名(使用英文) yourName
[✓] 请输入您的github的email地址 yourEmail
[✓] 请输入您demo的描述 这是一个测试的标准demo
在完成以上操作后, 可以得到这样的输出:
```
------------------
您新增的组件信息如下
==================
{
name : demoName
author : yourName
email : yourEmail
desc : 这是一个测试的标准demo
}
==================
[✓] Is this the config you want ? (Y/n) y
{
新建的demo文件位于 : /flutter go/lib/page_demo_package/demoName_yourName_1a29aa8e_32ae_4241_9c8a_5c9e1f92b096
demoId为 : 1a29aa8e_32ae_4241_9c8a_5c9e1f92b096
markdown中调用方式 : [demo:1a29aa8e_32ae_4241_9c8a_5c9e1f92b096]
}
```
您可以在任意详情页中, 通过以下方式调用
```
[demo: 1a29aa8e_32ae_4241_9c8a_5c9e1f92b096]
```
### createPage
使用方式同上. 通过该命令可以创建标准的详情页.您可以通过修改index.md进行动态的更新您所创建的详情页.
目录结构为
```
standard_pages/
├── index.dart (不可人为修改)
└── standard_sanfan_ee4feb8e_32ae_4241_9c8a_5c9e1f92b096
├── .page.json (不可人为修改)
├── index.dart (不可人为修改)
└── index.md (可修改)
```
### watch
监听并编译standard_pages与page_demo_package下的的文件改动. 动态处理demo目录与文件markdown转化等.
## 注意
- 在修改page_demo_package或者standard_pages目录下的文件操作时. 建议在flutterGo目录执行goCLi watch 开启文件动态编译
- name, author 字段必须使用英文开头, 不允许使用特殊符号. 正常的示范 name ='name_test' author = 'abcdefg';
- 暂时阶段demo与page,一经过创建不允许修改名称作者等信息. 凡是被收录进主分支的不允许被删除

17
go-cli/bin/goCli.dart Normal file
View File

@ -0,0 +1,17 @@
import 'package:args/args.dart'; // 使用其中两个类ArgParser和ArgResults
import 'package:args/command_runner.dart';
import '../src/cli_command_runder.dart';
ArgResults argResults; // 声明ArgResults类型的顶级变量保存解析的参数结果
// 同时argResults也是ArgResults的实例
void main(List<String> args) async {
// createDemo();
try {
await run(args);
} on UsageException catch (e) {
print(' ');
print(e.usage);
}
}

3
go-cli/config.json Normal file
View File

@ -0,0 +1,3 @@
{
"name": "sanfan"
}

23
go-cli/pubspec.yaml Normal file
View File

@ -0,0 +1,23 @@
name: goCli
description: bin_go
# The following defines the version and build number for your application.
# A version number is three numbers separated by dots, like 1.2.43
# followed by an optional build number separated by a +.
# Both the version and the builder number may be overridden in flutter
# build by specifying --build-name and --build-number, respectively.
# Read more about versioning at semver.org.
version: 1.0.0
executables:
goCli:
environment:
sdk: ">=2.1.0 <3.0.0"
dependencies:
args: '^1.5.1'
dart_inquirer: '^1.0.0'
watcher: ^0.9.7+10
dev_dependencies:

View File

@ -0,0 +1,95 @@
import 'dart:io';
import 'dart:convert';
import '../../utils/util.dart';
import '../config.dart';
import '../exception/demo.dart';
Future<List> buildDemoListJson() async {
List<FileSystemEntity> childList = await readeDirChildren(TARGET_DEMO_DIC, false);
List<String> demoPathList = [];
List<Map<String, dynamic>> detailList = []; // 存放所有demo的详情的列表
for(FileSystemEntity entity in childList) {
//文件、目录和链接都继承自FileSystemEntity
//FileSystemEntity.type静态函数返回值为FileSystemEntityType
//FileSystemEntityType有三个常量
//Directory、FILE、LINK、NOT_FOUND
//FileSystemEntity.isFile .isLink .isDerectory可用于判断类型
Uri url = entity.uri;
if (await FileSystemEntity.isDirectory(entity.path)) {
try {
await checkDemo(entity.path);
demoPathList.add(entity.path);
} on InvalidDemo catch (e) {
print("不存在 $e");
}
}
}
for (String dicPath in demoPathList) {
String target = '$dicPath/.demo.json';
String jsonString = await readeFile(target);
try {
Map<String, dynamic> item = json.decode(jsonString);
detailList.add(item);
} catch (err) {
print("err $err");
throw new Exception('$dicPath');
}
}
print("本次编译: 获取${detailList.length}条demo数据");
String demoTplString = renderDemosDart(detailList);
// 生成 page_demo_package/index.dart
writeContent2Path(TARGET_DEMO_DIC, 'index.dart', demoTplString.replaceAll(new RegExp('-'), '_'));
// 生成 page_demo_package/.demo.dart
writeContent2Path(TARGET_DEMO_DIC, '.demo.json', json.encode(detailList));
return Future(() => childList);
// return ;
}
String renderDemosDart(List<Map<String, dynamic>> data) {
// 自定义前缀 避免出现数字非法字符等
String pre = "StandardDemo";
String head = '';
String foot = 'var demoObjects = { \r\n';
for (int i = 0; i < data.length; i++) {
Map<String, dynamic> item = data[i];
String demoImportName = '${item['name']}_${item['id']}';
head += "import '${item['name']}_${item['author']}_${item['id']}/index.dart' as StandardDemo_$demoImportName;\r\n";
foot += " '${item['id']}': ${pre}_${demoImportName}.demoWidgets";
if (i != data.length - 1) {
foot += ',\r\n';
}
}
foot += "\r\n};";
return head + foot;
}
Future<bool> checkDemo(String path) async {
List files = [
'index.dart',
'.demo.json',
'src/'
];
bool success = true;
for (String name in files) {
bool isDic = name.indexOf('/') != -1;
bool isExist ;
String detailPath = '$path/$name';
if (isDic) {
isExist = await Directory(detailPath).exists();
} else {
isExist = await File(detailPath).exists();
}
if (!isExist) {
throw new InvalidDemo('$path$name not exit');
}
}
return Future(() => true);
}

View File

@ -0,0 +1,165 @@
import 'dart:io';
import 'dart:convert';
import 'package:path/path.dart' as p;
import '../../utils/util.dart';
import '../config.dart';
import '../exception/demo.dart';
Future<List> buildPageListJson() async {
List<FileSystemEntity> childList = await readeDirChildren(TARGET_PAGE_DIC, false);
List<String> pagePathList = [];
List<Map<String, dynamic>> detailList = []; // 存放所有demo的详情的列表
int errCount = 0;
int pageCount = 0;
for(FileSystemEntity entity in childList) {
//文件、目录和链接都继承自FileSystemEntity
//FileSystemEntity.type静态函数返回值为FileSystemEntityType
//FileSystemEntityType有三个常量
//Directory、FILE、LINK、NOT_FOUND
//FileSystemEntity.isFile .isLink .isDerectory可用于判断类型
if (await FileSystemEntity.isDirectory(entity.path)) {
pageCount++;
try {
await checkPage(entity.path);
pagePathList.add(entity.path);
} on InvalidDemo catch (e) {
errCount++;
print("不存在 $e");
}
}
}
for (String dicPath in pagePathList) {
String target = '$dicPath/.page.json';
String jsonString = await readeFile(target);
try {
Map<String, dynamic> item = json.decode(jsonString);
detailList.add(item);
} catch (err) {
print("err $err");
throw new Exception('$dicPath');
}
}
print("本次编译: 总${pageCount}个界面, 成功${detailList.length}条, 失败${errCount}");
String demoTplString = renderPagesDart(detailList);
//
// // 生成 page_demo_package/index.dart
writeContent2Path(TARGET_PAGE_DIC, 'index.dart', demoTplString.replaceAll(new RegExp('-'), '_'));
// // 生成 page_demo_package/.demo.dart
writeContent2Path(TARGET_PAGE_DIC, '.pages.json', json.encode(detailList));
return Future(() => childList);
// return ;
}
String renderPagesDart(List<Map<String, dynamic>> data) {
// 自定义前缀 避免出现数字非法字符等
String pre = "StandardPage";
String head = '';
String foot = """
class StandardPages {
Map<String, String> standardPages;
Map<String, String> getPages() {
return {
""";
for (int i = 0; i < data.length; i++) {
Map<String, dynamic> item = data[i];
String demoImportName = '${item['name']}_${item['id']}';
head += "import '${item['name']}_${item['author']}_${item['id']}/index.dart' as ${pre}_$demoImportName;\r\n";
foot += "\t\t\t'${item['id']}': ${pre}_${demoImportName}.getMd()";
if (i != data.length - 1) {
foot += ',\r\n';
}
}
foot += """\r
};
}
}
""";
return head + foot;
}
Future<bool> checkPage(String path) async {
List files = [
'index.dart',
'index.md',
'.page.json'
];
for (String name in files) {
bool isDic = name.indexOf('/') != -1;
bool isExist ;
String detailPath = '$path/$name';
if (isDic) {
isExist = await Directory(detailPath).exists();
} else {
isExist = await File(detailPath).exists();
}
if (!isExist) {
throw new InvalidDemo('$path$name not exit');
}
}
return Future(() => true);
}
Future<void> transformMd2Dart(String path) async{
print("pathLL: $path");
String mdContent = await readeFile(p.absolute(path,'index.md'));
String result = """
String getMd() {
return \"\"\"
${mdContent}\"\"\";
}
""";
writeContent2Path(p.absolute(path), 'index.dart', result);
return '';
}
//main() {
// buildPageListJson();
//}
////
//// Created with Android Studio.
//// User: 三帆
//// Date: 25/05/2019
//// Time: 21:23
//// email: sanfan.hx@alibaba-inc.com
//// target: 该文件用在打包后的代码中.日常开发的时候, 获取markdown不走该目录
////
//import 'package:flutter/material.dart';
//import 'page1_hanxu_172ba42f_0520_401e_b568_ba7f7f6835e4/index.dart' as page1_hanxu_172ba42f_0520_401e_b568_ba7f7f6835e4;
//
//
//class StandardPages {
// Map<String, String> standardPages;
// Map<String, String> getPages() {
// return {
// 'page1_hanxu_172ba42f_0520_401e_b568_ba7f7f6835e4': page1_hanxu_172ba42f_0520_401e_b568_ba7f7f6835e4.stringMd
// };
// }
//}
//var stringMd = """# page
//
//this is page markdown
//
//you can load demo like this
//
//```
//[demo: xxxid]
//```
//
//""";
//

View File

@ -0,0 +1,33 @@
import 'dart:async';
import 'package:args/args.dart';
import 'package:args/command_runner.dart';
import './version.dart';
import './command/create_demo.dart';
import './command/create_page.dart';
import './command/watch_md.dart';
Future<int> run(List<String> args) => _CommandRunner().run(args);
class _CommandRunner extends CommandRunner<int> {
_CommandRunner() : super('goCli', 'A tool to develop flutter go projects.') {
argParser.addFlag('version',
negatable: false, help: 'Prints the version of goCi.');
addCommand(CreateDemoCommand());
addCommand(CreatePageCommand());
addCommand(WatchCommand());
}
@override
Future<int> runCommand(ArgResults topLevelResults) async {
if (topLevelResults['version'] as bool) {
return 0;
}
// In the case of `help`, `null` is returned. Treat that as success.
return await super.runCommand(topLevelResults) ?? 0;
}
}

View File

@ -0,0 +1,137 @@
import 'dart:io';
import 'package:args/args.dart'; // 使用其中两个类ArgParser和ArgResults
import 'package:dart_inquirer/dart_inquirer.dart';
import 'package:console/console.dart';
import 'package:path/path.dart' as p;
import 'package:args/command_runner.dart';
import '../build/build_demo_list.dart';
import '../config.dart';
import '../../utils/util.dart';
ArgResults argResults; // 声明ArgResults类型的顶级变量保存解析的参数结果
class DemoDetail {
String name;
String author;
String email;
String desc;
String id;
DemoDetail.fromJson(Map<dynamic, dynamic> json) {
name = json['name'].trim();
author = json['author'].trim();
email = json['email'].trim();
desc = json['desc'].trim();
id = json['id'] ?? generateId();
}
}
// 同时argResults也是ArgResults的实例
void createDemo() async {
Map environmentVars = Platform.environment;
List<Question> questions = [
InputQuestion('name', '请输入新增加的demo名称?'),
InputQuestion('author', '请输入您的姓名(使用英文)'),
InputQuestion('email', '请输入您的github的email地址'),
InputQuestion('desc', '请输入您demo的描述'),
];
Map<dynamic, dynamic> answers = {};
// 获取初次信息
Prompt prompt = Prompt(questions);
DemoDetail demoDetail;
answers = await prompt.execute();
print(Seperator());
print('您新增的组件信息如下');
print(Seperator('='));
prettyPrintJson(answers);
print(Seperator('='));
questions =[ConfirmQuestion('confirm', 'Is this the config you want ?')];
prompt = Prompt(questions);
Map confirmAnswers = await prompt.execute();
if (confirmAnswers['confirm'] != true) {
return createDemo();
}
demoDetail = DemoDetail.fromJson(answers);
var demoPath = '$TARGET_DEMO_DIC/${demoDetail.name}_${demoDetail.author}_${demoDetail.id}';
// 创建root文件
await createFile(demoPath);
await createFile('$demoPath/src');
// print("demoPath>>>> ${environmentVars['PWD']}/${demoPath}");
writeContent2Path('$demoPath/src/', 'index.dart', """
import 'package:flutter/material.dart';
class Demo extends StatefulWidget {
@override
_State createState() => _State();
}
class _State extends State<Demo> {
@override
Widget build(BuildContext context) {
return Container(
child: Text("this is flutter go init demo"),
);
}
}
""");
writeContent2Path('$demoPath/', '.demo.json', """
{
"name": "${demoDetail.name}",
"screenShot": "",
"author":"${demoDetail.author}",
"email": "${demoDetail.email}",
"desc": "${demoDetail.desc}",
"id": "${demoDetail.id}"
}
""");
writeContent2Path('$demoPath/', 'index.dart', """
//
// Created with flutter go cli
// User: ${demoDetail.author}
// Time: ${new DateTime.now()}
// email: ${demoDetail.email}
// desc: ${demoDetail.desc}
//
import 'src/index.dart';
var demoWidgets = [
new Demo()
];
""");
// format('{color.red}Invalid demo happends: $details {color.normal}');
prettyPrintJson({
'新建的demo文件位于': p.absolute((demoPath)),
'demoId为': demoDetail.id,
'markdown中调用方式': '[demo:${demoDetail.id}]'
});
buildDemoListJson();
}
class CreateDemoCommand extends Command<int> {
@override
final name = 'createDemo';
@override
final description = '新增flutter go demo.';
CreateDemoCommand() {
}
@override
Future<int> run() async {
createDemo();
return 0;
}
}

View File

@ -0,0 +1,124 @@
import 'dart:io';
import 'package:args/args.dart'; // 使用其中两个类ArgParser和ArgResults
import 'package:dart_inquirer/dart_inquirer.dart';
import 'package:args/command_runner.dart';
import '../build/build_page_list.dart';
import '../config.dart';
import '../../utils/util.dart';
var pageMarkdown = """
# page
this is page markdown
you can load demo like this
```
[demo: xxxid]
```
""";
ArgResults argResults; // 声明ArgResults类型的顶级变量保存解析的参数结果
class PageDetail {
String name;
String author;
String title;
String email;
String desc;
String id;
PageDetail.fromJson(Map<dynamic, dynamic> json) {
name = json['name'];
author = json['author'];
title = json['title'];
email = json['email'];
desc = json['desc'];
id = json['id'] ?? generateId();
}
}
// 同时argResults也是ArgResults的实例
void createPage() async {
Map environmentVars = Platform.environment;
List<Question> questions = [
InputQuestion('name', '请输入文件名称?'),
InputQuestion('title', '请输入界面名称?'),
InputQuestion('author', '请输入您的姓名(使用英文)'),
InputQuestion('email', '请输入您的email地址'),
InputQuestion('desc', '请输入您界面的简要'),
];
Map<dynamic, dynamic> answers = {};
// 获取初次信息
Prompt prompt = Prompt(questions);
PageDetail pageDetail;
answers = await prompt.execute();
print(Seperator());
print('您新增的界面信息如下');
print(Seperator('='));
prettyPrintJson(answers);
print(Seperator('='));
questions =[ConfirmQuestion('confirm', 'Is this the config you want ?')];
prompt = Prompt(questions);
Map confirmAnswers = await prompt.execute();
if (confirmAnswers['confirm'] != true) {
return createPage();
}
pageDetail = PageDetail.fromJson(answers);
var demoPath = '$TARGET_PAGE_DIC/${pageDetail.name}_${pageDetail.author}_${pageDetail.id}';
// 创建root文件
await createFile(demoPath);
pageMarkdown = await readeFile("${environmentVars['PWD']}/go-cli/utils/tpl.md");
writeContent2Path('$demoPath/', 'index.dart', """
String getMd() {
return \"\"\"
${pageMarkdown}\"\"\";
}
""");
writeContent2Path('$demoPath/', '.page.json', """
{
"name": "${pageDetail.name}",
"screenShot": "",
"author":"${pageDetail.author}",
"title":"${pageDetail.title}",
"email": "${pageDetail.email}",
"desc": "${pageDetail.desc}",
"id": "${pageDetail.id}"
}
""");
writeContent2Path('$demoPath/', 'index.md', pageMarkdown);
buildPageListJson();
prettyPrintJson({
'界面位于': demoPath,
'Id': pageDetail.id,
'文件夹名称': '${pageDetail.name}_${pageDetail.author}_${pageDetail.id}'
});
}
class CreatePageCommand extends Command<int> {
@override
final name = 'createPage';
@override
final description = '新增flutter go page.';
CreateDemoCommand() {
}
@override
Future<int> run() async {
createPage();
return 0;
}
}

View File

@ -0,0 +1,102 @@
//
// Created with Android Studio.
// User: 三帆
// Date: 08/06/2019
// Time: 16:34
// email: sanfan.hx@alibaba-inc.com
// target: 监听demo与md
//
import 'package:path/path.dart' as p;
import 'package:args/command_runner.dart';
import '../build/build_demo_list.dart';
import '../build/build_page_list.dart';
import 'package:watcher/watcher.dart';
import '../config.dart';
import 'dart:async';
void watch() {
Timer _changeTimer;
List<Map<String, String>> config = [
{
"type": "demo",
"path": '$TARGET_DEMO_DIC'
}
];
List<String> demoIgnore = [
'.demo.json',
'index.dart',
'info.json',
'readme.md',
];
var watcherDemo = DirectoryWatcher(p.absolute('$TARGET_DEMO_DIC'));
watcherDemo.events.listen((event) {
if (isIgnore(demoIgnore, TARGET_DEMO_DIC, event.path)) {
return ;
}
if (event.type == ChangeType.ADD || event.type == ChangeType.REMOVE) {
if (_changeTimer != null && _changeTimer.isActive) {
_changeTimer.cancel();
}
_changeTimer = new Timer(Duration(milliseconds: 1000), () {
buildDemoListJson();
});
}
}, onError: (error) {
print('watch 发生错误: $error');
});
List<String> pageIgnore = [
'index.dart',
'.pages.json'
];
var watcherPage = DirectoryWatcher(p.absolute('$TARGET_PAGE_DIC'));
watcherPage.events.listen((event) {
if (isIgnore(pageIgnore, TARGET_PAGE_DIC, event.path)) {
return ;
}
if (event.type == ChangeType.ADD || event.type == ChangeType.REMOVE) {
print("buildPageListJson:::");
buildPageListJson();
return ;
}
if (event.type == ChangeType.MODIFY) {
if (event.path.contains('index.md')) {
transformMd2Dart(event.path.replaceAll('/index.md', ''));
}
return ;
}
print(event);
});
}
bool isIgnore (List<String> ignorePath, parentPath, currentPath) {
for (String ignore in ignorePath) {
String path = p.absolute(parentPath, ignore);
if (currentPath.contains(path)) {
// print("修改的文件, 是忽略列表中的文件, 跳过编译!");
return true;
}
}
return false;
}
class WatchCommand extends Command<int> {
@override
final name = 'watch';
@override
final description = '动态生成文档与demo相关';
CreateDemoCommand() {
}
@override
Future<int> run() async {
watch();
return 0;
}
}

4
go-cli/src/config.dart Normal file
View File

@ -0,0 +1,4 @@
// 生成Demo的目录位置
const TARGET_PAGE_DIC = 'lib/standard_pages';
const TARGET_DEMO_DIC = 'lib/page_demo_package';

View File

@ -0,0 +1,21 @@
//
// Created with Android Studio.
// User: 三帆
// Date: 25/05/2019
// Time: 21:20
// email: sanfan.hx@alibaba-inc.com
// target: xxx
//
import 'package:console/console.dart';
class InvalidDemo implements Exception {
final String details;
InvalidDemo(this.details);
@override
String toString() {
return format('{color.red}Invalid demo happends: $details {color.normal}');
}
}

1
go-cli/src/version.dart Normal file
View File

@ -0,0 +1 @@
const packageVersion = '1.0.0';

48
go-cli/utils/tpl.md Normal file
View File

@ -0,0 +1,48 @@
# 标准的详情页
您可以在这个界面中, 编写大多数的markdown文案, 他会在 **goCli watch** 下同步被编译成 **dart** 文件
您可以通过goCli创建详情页所需要的demo
```
goCLi createDemo
```
在flutter go 根文件下通过命令行输入以上命令可以进行以下操作
[✓] 请输入新增加的demo名称? demoName
[✓] 请输入您的姓名(使用英文) yourName
[✓] 请输入您的github的email地址 yourEmail
[✓] 请输入您demo的描述 这是一个测试的标准demo
在完成以上操作后, 可以得到这样的输出:
```
------------------
您新增的组件信息如下
==================
{
name : demoName
author : yourName
email : yourEmail
desc : 这是一个测试的标准demo
}
==================
[✓] Is this the config you want ? (Y/n) y
{
新建的demo文件位于 : /flutter go/lib/page_demo_package/demoName_yourName_1a29aa8e_32ae_4241_9c8a_5c9e1f92b096
demoId为 : 1a29aa8e_32ae_4241_9c8a_5c9e1f92b096
markdown中调用方式 : [demo:1a29aa8e_32ae_4241_9c8a_5c9e1f92b096]
}
```
您可以在任意详情页中, 通过以下方式调用
```
[demo: 1a29aa8e_32ae_4241_9c8a_5c9e1f92b096]
```

64
go-cli/utils/util.dart Normal file
View File

@ -0,0 +1,64 @@
import 'dart:io';
import 'dart:math';
Future<bool> createFile(String path) async {
final tempDic = new Directory(path);
var exits = await tempDic.exists();
if (exits) {
print("文件当前已存在");
return Future(() => false);
}
await tempDic.createSync(recursive: true);
return Future(() => true);
}
Future<String> readeFile(String path) async {
String content = await new File(path).readAsString();
return Future(() => content);
}
// 该文件调用的时候. 必须存在父级文件夹
Future<void> writeContent2Path(String path,String fileName, String content) async {
var file = File('$path/$fileName');
var sink = file.openWrite();
sink.write(content);
await sink.flush();
await sink.close();
return Future.value();
}
String generateId() {
int d = DateTime.now().millisecondsSinceEpoch;
var random = new Random(1);
var template = 'xxxxxxxx_xxxx_4xxx_yxxx_xxxxxxxxxxxx';
var id = template.replaceAllMapped(new RegExp(r"[x|y]"), (c) {
var r = ((d + random.nextDouble() * 16) % 16).toInt();
d = d ~/ 16;
var t = c.group(0) == 'x' ? r : (1 & 0x3 | 0x8);
return t.toRadixString(16);
});
return id;
}
/// 该文件调用的时候. 必须存在父级文件夹
/// @param path 目录地址
/// @param recursive 是否显示子目录或者子文件
Future<List<FileSystemEntity>> readeDirChildren(String path, [bool recursive = false]) async {
try {
var dirParent = Directory(path);
Stream<FileSystemEntity> entityList = dirParent.list(recursive: false, followLinks: false);
List<FileSystemEntity> fileChildren = [];
await for(FileSystemEntity entity in entityList) {
fileChildren.add(entity);
}
return Future(() => fileChildren);
} catch (e) {
print('读取文件失败 ${e.toString()}');
return Future(() => []);
}
}
//void main() {
// readeFile('/Users/ontwo/Documents/ali/flutter/flutter-common-widgets-app/LICENSE');
//}

View File

@ -1,2 +1,3 @@
#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
#include "Generated.xcconfig"
#include "../Pods/Target\ Support\ Files/Pods-Runner/Pods-Runner.debug.xcconfig"

View File

@ -1,2 +1,3 @@
#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
#include "Generated.xcconfig"
#include "../Pods/Target\ Support\ Files/Pods-Runner/Pods-Runner.release.xcconfig"

View File

@ -32,6 +32,16 @@
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
FA240D7922F2857D003025F3 /* Flutter.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FAD0115F22F1B9DF0016E673 /* Flutter.framework */; };
FA240D7A22F2857D003025F3 /* Flutter.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = FAD0115F22F1B9DF0016E673 /* Flutter.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
FA240D7B22F28581003025F3 /* App.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FAD0116122F1B9DF0016E673 /* App.framework */; };
FA240D7C22F28581003025F3 /* App.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = FAD0116122F1B9DF0016E673 /* App.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
FAD0116422F1B9DF0016E673 /* Debug.xcconfig in Resources */ = {isa = PBXBuildFile; fileRef = FAD0115E22F1B9DF0016E673 /* Debug.xcconfig */; };
FAD0116522F1B9DF0016E673 /* Flutter.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FAD0115F22F1B9DF0016E673 /* Flutter.framework */; };
FAD0116622F1B9DF0016E673 /* Release.xcconfig in Resources */ = {isa = PBXBuildFile; fileRef = FAD0116022F1B9DF0016E673 /* Release.xcconfig */; };
FAD0116722F1B9DF0016E673 /* App.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FAD0116122F1B9DF0016E673 /* App.framework */; };
FAD0116822F1B9DF0016E673 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = FAD0116222F1B9DF0016E673 /* AppFrameworkInfo.plist */; };
FAD0116922F1B9DF0016E673 /* Generated.xcconfig in Resources */ = {isa = PBXBuildFile; fileRef = FAD0116322F1B9DF0016E673 /* Generated.xcconfig */; };
/* End PBXBuildFile section */
/* Begin PBXCopyFilesBuildPhase section */
@ -41,6 +51,8 @@
dstPath = "";
dstSubfolderSpec = 10;
files = (
FA240D7C22F28581003025F3 /* App.framework in Embed Frameworks */,
FA240D7A22F2857D003025F3 /* Flutter.framework in Embed Frameworks */,
);
name = "Embed Frameworks";
runOnlyForDeploymentPostprocessing = 0;
@ -80,6 +92,12 @@
97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
A4868A865F318D337B7500AF /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = "<group>"; };
B7B77D3AD1E975E5BE03D770 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = "<group>"; };
FAD0115E22F1B9DF0016E673 /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = "<group>"; };
FAD0115F22F1B9DF0016E673 /* Flutter.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Flutter.framework; sourceTree = "<group>"; };
FAD0116022F1B9DF0016E673 /* Release.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = "<group>"; };
FAD0116122F1B9DF0016E673 /* App.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = App.framework; sourceTree = "<group>"; };
FAD0116222F1B9DF0016E673 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = AppFrameworkInfo.plist; sourceTree = "<group>"; };
FAD0116322F1B9DF0016E673 /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Generated.xcconfig; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@ -88,6 +106,10 @@
buildActionMask = 2147483647;
files = (
17DB4C25EDB98B3648015B9E /* libPods-Runner.a in Frameworks */,
FAD0116722F1B9DF0016E673 /* App.framework in Frameworks */,
FAD0116522F1B9DF0016E673 /* Flutter.framework in Frameworks */,
FA240D7922F2857D003025F3 /* Flutter.framework in Frameworks */,
FA240D7B22F28581003025F3 /* App.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -127,6 +149,7 @@
97C146E51CF9000F007C117D = {
isa = PBXGroup;
children = (
FAD0115D22F1B9DF0016E673 /* Flutter */,
0828E485220692A700A59437 /* launch */,
084A20872202E4FD00428FF5 /* flutter go.png */,
97C146F01CF9000F007C117D /* Runner */,
@ -180,6 +203,19 @@
path = Pods;
sourceTree = "<group>";
};
FAD0115D22F1B9DF0016E673 /* Flutter */ = {
isa = PBXGroup;
children = (
FAD0115E22F1B9DF0016E673 /* Debug.xcconfig */,
FAD0115F22F1B9DF0016E673 /* Flutter.framework */,
FAD0116022F1B9DF0016E673 /* Release.xcconfig */,
FAD0116122F1B9DF0016E673 /* App.framework */,
FAD0116222F1B9DF0016E673 /* AppFrameworkInfo.plist */,
FAD0116322F1B9DF0016E673 /* Generated.xcconfig */,
);
path = Flutter;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
@ -250,6 +286,7 @@
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
FAD0116622F1B9DF0016E673 /* Release.xcconfig in Resources */,
0828E4A0220692B500A59437 /* iPad Landscape.png in Resources */,
0828E4A1220692B500A59437 /* iPhone XR Portrait.png in Resources */,
0828E49F220692B500A59437 /* iPhone XS Max Landscape.png in Resources */,
@ -260,14 +297,17 @@
0828E49E220692B500A59437 /* iPad Portrait@2x.png in Resources */,
0828E49D220692B500A59437 /* iPhone Portrait@2x.png in Resources */,
0828E499220692B500A59437 /* iPhone Portrait-Retina HD 4.7.png in Resources */,
FAD0116822F1B9DF0016E673 /* AppFrameworkInfo.plist in Resources */,
084A20882202E4FD00428FF5 /* flutter go.png in Resources */,
0828E497220692B500A59437 /* iPhone X_XS Landscape.png in Resources */,
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */,
0828E4A52206936100A59437 /* Images.xcassets in Resources */,
FAD0116922F1B9DF0016E673 /* Generated.xcconfig in Resources */,
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */,
0828E49B220692B500A59437 /* iPad Landscape@2x.png in Resources */,
0828E495220692B500A59437 /* iPhone Portrait-Retina 4.png in Resources */,
0828E498220692B500A59437 /* iPhone XS Max Portrait.png in Resources */,
FAD0116422F1B9DF0016E673 /* Debug.xcconfig in Resources */,
94722E5C22511D3600F63900 /* GoogleService-Info.plist in Resources */,
0828E496220692B500A59437 /* iPad Portrait.png in Resources */,
0828E49C220692B500A59437 /* iPhone Landscape-Retina HD 5.5.png in Resources */,
@ -289,11 +329,11 @@
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" thin";
shellScript = "/bin/sh \"/Users/nealyang/development/flutter/packages/flutter_tools/bin/xcode_backend.sh\" thin\n";
};
9740EEB61CF901F6004384FC /* Run Script */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
buildActionMask = 12;
files = (
);
inputPaths = (
@ -303,7 +343,7 @@
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build\n";
};
B34D03FFEA3831330E64E5C7 /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
@ -506,7 +546,7 @@
};
97C147061CF9000F007C117D /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = A4868A865F318D337B7500AF /* Pods-Runner.debug.xcconfig */;
baseConfigurationReference = FAD0115E22F1B9DF0016E673 /* Debug.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = Launch2;
@ -537,7 +577,7 @@
};
97C147071CF9000F007C117D /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = B7B77D3AD1E975E5BE03D770 /* Pods-Runner.release.xcconfig */;
baseConfigurationReference = FAD0116022F1B9DF0016E673 /* Release.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = Launch2;

View File

@ -1,5 +1,5 @@
class Api{
// static const String BASE_URL = 'http://flutter-go.alibaba.net/';
// static const String BASE_URL = 'http://flutter-go.alibaba.net/';
static const String BASE_URL = 'https://flutter-go.pub/api/';
static const String DO_LOGIN = BASE_URL+'doLogin';//登陆
@ -15,7 +15,7 @@ class Api{
static const String FEEDBACK = BASE_URL+'auth/feedback';//建议反馈
static const String LOTOUT = BASE_URL+'logout';//退出登陆
// static const String LOTOUT = BASE_URL+'logout';//退出登陆
static const String GET_ALL_COLLECTION = BASE_URL+'auth/getAllUserCollection';//获取全部收藏
@ -23,9 +23,14 @@ class Api{
static const String ADD_COLLECTION = BASE_URL+'auth/addCollection';//添加收藏
static const String CHECK_COLLECTED = BASE_URL+'auth/checkCollected';//校验收藏
static const String CHECK_COLLECTED = BASE_URL+'checkCollected';//校验收藏
static const String SET_THEMECOLOR = BASE_URL+'auth/setThemeColor';//设置主题颜色
static const String GET_THEMECOLOR = BASE_URL +'/getThemeColor';//获取主题颜色
}
static const String GET_WIDGET_TREE = 'http://flutter-go.alibaba.net/' + 'getCateList';//获取widget列表树
static const String SEARCH_WIDGET = BASE_URL+'searchWidget';//搜索组件
}

View File

@ -1,11 +1,11 @@
import 'package:flutter/material.dart';
import '../model/cat.dart';
import '../resources/widget_name_to_icon.dart';
import '../components/widget_item_container.dart';
import '../model/widget.dart';
class CateCard extends StatefulWidget {
final Cat category;
final CategoryComponent category;
CateCard({@required this.category});
@override
_CateCardState createState() => _CateCardState();
@ -13,28 +13,15 @@ class CateCard extends StatefulWidget {
class _CateCardState extends State<CateCard> {
// 一级菜单目录下的二级Cat集合
List<Cat> _firstChildList = new List();
CatControlModel catControl = new CatControlModel();
List<CommonItem> _firstChildList;
@override
void initState() {
super.initState();
getFirstChildCategoriesByParentId();
_firstChildList = widget.category.children;
}
// 获取一层目录下的二级内容
getFirstChildCategoriesByParentId() async {
int parentId = widget.category.id;
// 构建查询条件
Cat childCateCondition = new Cat(parentId: parentId);
List<Cat> list = await catControl.getList(childCateCondition);
if (list.isNotEmpty&&list.length>=1 && this.mounted) {
setState(() {
_firstChildList = list;
});
}
}
@override
Widget build(BuildContext context) {
@ -43,7 +30,6 @@ class _CateCardState extends State<CateCard> {
//首字母转为大写
widget.category.name.substring(0, 1),
widget.category.name.substring(0, 1).toUpperCase());
return Container(
width: screenWidth,
padding: const EdgeInsets.symmetric(vertical: 10.0, horizontal: 10.0),
@ -119,9 +105,8 @@ class _CateCardState extends State<CateCard> {
),
),
child: WidgetItemContainer(
categories: this._firstChildList,
columnCount: 3,
isWidgetPoint:false
commonItems: this._firstChildList,
columnCount: 3
),
);
}

View File

@ -8,11 +8,12 @@ import '../model/widget.dart';
import '../widgets/index.dart';
import '../components/widget_item_container.dart';
enum CateOrWigdet { Cat, WidgetDemo }
class CategoryHome extends StatefulWidget {
CategoryHome(this.name);
final String name;
CategoryHome(this.token);
final String token;
@override
_CategoryHome createState() => new _CategoryHome();
@ -21,12 +22,11 @@ class CategoryHome extends StatefulWidget {
class _CategoryHome extends State<CategoryHome> {
String title = '';
// 显示列表 cat or widget;
List<Cat> categories = [];
List<WidgetPoint> widgetPoints = [];
List<Cat> catHistory = new List();
List<CommonItem> items = [];
List<Object> widgetPoints = [];
List<CommonItem> catHistory = new List();
CatControlModel catControl = new CatControlModel();
WidgetControlModel widgetControl = new WidgetControlModel();
// 所有的可用demos;
List widgetDemosList = new WidgetDemoList().getDemos();
@ -34,80 +34,60 @@ class _CategoryHome extends State<CategoryHome> {
void initState() {
super.initState();
// 初始化加入顶级的name
this.getCatByName(widget.name).then((Cat cat) {
catHistory.add(cat);
searchCatOrWigdet();
print("这是新界面的id:>>> ${widget.token}");
CommonItem targetGroup = Application.widgetTree.find(widget.token) ?? [];
print("targetGroup::: $targetGroup");
catHistory.add(
targetGroup
);
this.setState(() {
items = targetGroup.children;
});
searchCatOrWidget();
}
Future<Cat> getCatByName(String name) async {
return await catControl.getCatByName(name);
}
Future<bool> back() {
if (catHistory.length == 1) {
return Future<bool>.value(true);
}
catHistory.removeLast();
searchCatOrWigdet();
return Future<bool>.value(false);
// if (catHistory.length == 1) {
// return Future<bool>.value(true);
// }
// catHistory.removeLast();
// searchCatOrWidget();
return Future<bool>.value(true);
}
void go(Cat cat) {
void go(CommonItem cat) {
catHistory.add(cat);
searchCatOrWigdet();
searchCatOrWidget();
}
void searchCatOrWigdet() async {
// 假设进入这个界面的parent一定存在
Cat parentCat = catHistory.last;
void searchCatOrWidget() async {
CommonItem widgetTree = Application.widgetTree;
// 假设进入这个界面的parent一定存在
CommonItem targetGroup = catHistory.last;
// 继续搜索显示下一级depth: depth + 1, parentId: parentCat.id
List<Cat> _categories =
await catControl.getList(new Cat(parentId: parentCat.id));
List<WidgetPoint> _widgetPoints = new List();
if (_categories.isEmpty) {
_widgetPoints =
await widgetControl.getList(new WidgetPoint(catId: parentCat.id));
}
this.setState(() {
categories = _categories;
title = parentCat.name;
widgetPoints = _widgetPoints;
title = targetGroup.name;
});
}
void onCatgoryTap(Cat cat) {
void onCatgoryTap(CommonItem cat) {
go(cat);
}
void onWidgetTap(WidgetPoint widgetPoint) {
String targetName = widgetPoint.name;
String targetRouter = '/category/error/404';
widgetDemosList.forEach((item) {
// print("targetRouter = item.routerName> ${[item.name,targetName]}");
if (item.name == targetName) {
targetRouter = item.routerName;
}
});
Application.router.navigateTo(context, "$targetRouter");
}
Widget _buildContent() {
WidgetItemContainer wiContaienr = WidgetItemContainer(
columnCount: 3,
categories: categories,
isWidgetPoint:false
commonItems: items
);
if (widgetPoints.length > 0) {
wiContaienr = WidgetItemContainer(
categories: widgetPoints,
columnCount: 3,
isWidgetPoint:true
);
}
return Container(
padding: const EdgeInsets.only(bottom: 10.0, top: 5.0),
decoration: BoxDecoration(
@ -122,14 +102,18 @@ class _CategoryHome extends State<CategoryHome> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(title),
title: Text("$title"),
),
body: WillPopScope(
onWillPop: () {
return back();
},
child: ListView(
children: <Widget>[
_buildContent(),

View File

@ -0,0 +1,27 @@
// Copyright 2017 Google, Inc. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@ -0,0 +1,39 @@
# Flutter Markdown
[![pub package](https://img.shields.io/pub/v/flutter_markdown.svg)](https://pub.dartlang.org/packages/flutter_markdown)
[![Build Status](https://travis-ci.org/flutter/flutter_markdown.svg?branch=master)](https://travis-ci.org/flutter/flutter_markdown)
A markdown renderer for Flutter. It supports the
[original format](https://daringfireball.net/projects/markdown/), but no inline
html.
## Getting Started
Using the Markdown widget is simple, just pass in the source markdown as a
string:
new Markdown(data: markdownSource);
If you do not want the padding or scrolling behavior, use the MarkdownBody
instead:
new MarkdownBody(data: markdownSource);
By default, Markdown uses the formatting from the current material design theme,
but it's possible to create your own custom styling. Use the MarkdownStyle class
to pass in your own style. If you don't want to use Markdown outside of material
design, use the MarkdownRaw class.
## Image support
The `Img` tag only supports the following image locations:
* From the network: Use a URL prefixed by either `http://` or `https://`.
* From local files on the device: Use an absolute path to the file, for example by
concatenating the file name with the path returned by a known storage location,
such as those provided by the [`path_provider`](https://pub.dartlang.org/packages/path_provider)
plugin.
* From image locations referring to bundled assets: Use an asset name prefixed by `resource:`.
like `resource:assets/image.png`.

View File

@ -0,0 +1,10 @@
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
/// A library to render markdown formatted text.
library flutter_markdown;
export 'src/builder.dart';
export 'src/style_sheet.dart';
export 'src/widget.dart';

View File

@ -0,0 +1,376 @@
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:io';
import 'package:flutter/gestures.dart';
import 'package:flutter/widgets.dart';
import 'package:markdown/markdown.dart' as md;
import 'package:path/path.dart' as p;
import 'style_sheet.dart';
typedef Widget DemoBuilder(Map<String, dynamic> attrs);
final Set<String> _kBlockTags = new Set<String>.from(<String>[
'p',
'h1',
'h2',
'h3',
'h4',
'h5',
'h6',
'li',
'blockquote',
'pre',
'ol',
'ul',
'hr',
]);
const List<String> _kListTags = const <String>['ul', 'ol'];
bool _isBlockTag(String tag) => _kBlockTags.contains(tag);
bool _isListTag(String tag) => _kListTags.contains(tag);
class _BlockElement {
_BlockElement(this.tag);
final String tag;
final List<Widget> children = <Widget>[];
int nextListIndex = 0;
}
/// A collection of widgets that should be placed adjacent to (inline with)
/// other inline elements in the same parent block.
///
/// Inline elements can be textual (a/em/strong) represented by [RichText]
/// widgets or images (img) represented by [Image.network] widgets.
///
/// Inline elements can be nested within other inline elements, inheriting their
/// parent's style along with the style of the block they are in.
///
/// When laying out inline widgets, first, any adjacent RichText widgets are
/// merged, then, all inline widgets are enclosed in a parent [Wrap] widget.
class _InlineElement {
_InlineElement(this.tag, {this.style});
final String tag;
/// Created by merging the style defined for this element's [tag] in the
/// delegate's [MarkdownStyleSheet] with the style of its parent.
final TextStyle style;
final List<Widget> children = <Widget>[];
}
/// A delegate used by [MarkdownBuilder] to control the widgets it creates.
abstract class MarkdownBuilderDelegate {
/// Returns a gesture recognizer to use for an `a` element with the given
/// `href` attribute.
GestureRecognizer createLink(String href);
/// Returns formatted text to use to display the given contents of a `pre`
/// element.
///
/// The `styleSheet` is the value of [MarkdownBuilder.styleSheet].
TextSpan formatText(MarkdownStyleSheet styleSheet, String code);
}
/// Builds a [Widget] tree from parsed Markdown.
///
/// See also:
///
/// * [Markdown], which is a widget that parses and displays Markdown.
class MarkdownBuilder implements md.NodeVisitor {
/// Creates an object that builds a [Widget] tree from parsed Markdown.
MarkdownBuilder({
this.delegate,
this.styleSheet,
this.imageDirectory,
this.demoParser
});
/// A delegate that controls how link and `pre` elements behave.
final MarkdownBuilderDelegate delegate;
/// Defines which [TextStyle] objects to use for each type of element.
final MarkdownStyleSheet styleSheet;
final DemoBuilder demoParser;
/// The base directory holding images referenced by Img tags with local file paths.
final Directory imageDirectory;
final List<String> _listIndents = <String>[];
final List<_BlockElement> _blocks = <_BlockElement>[];
final List<_InlineElement> _inlines = <_InlineElement>[];
final List<GestureRecognizer> _linkHandlers = <GestureRecognizer>[];
/// Returns widgets that display the given Markdown nodes.
///
/// The returned widgets are typically used as children in a [ListView].
List<Widget> build(List<md.Node> nodes) {
_listIndents.clear();
_blocks.clear();
_inlines.clear();
_linkHandlers.clear();
_blocks.add(new _BlockElement(null));
for (md.Node node in nodes) {
assert(_blocks.length == 1);
node.accept(this);
}
assert(_inlines.isEmpty);
return _blocks.single.children;
}
@override
void visitText(md.Text text) {
if (_blocks.last.tag == null) // Don't allow text directly under the root.
return;
_addParentInlineIfNeeded(_blocks.last.tag);
final TextSpan span = _blocks.last.tag == 'pre'
? delegate.formatText(styleSheet, text.text)
: new TextSpan(
style: _inlines.last.style,
text: text.text,
recognizer: _linkHandlers.isNotEmpty ? _linkHandlers.last : null,
);
_inlines.last.children.add(new RichText(
textScaleFactor: styleSheet.textScaleFactor,
text: span,
));
}
@override
bool visitElementBefore(md.Element element) {
// print("visitElementBefore ${element.tag}");
final String tag = element.tag;
if (_isBlockTag(tag)) {
_addAnonymousBlockIfNeeded(styleSheet.styles[tag]);
if (_isListTag(tag))
_listIndents.add(tag);
_blocks.add(new _BlockElement(tag));
} else {
_addParentInlineIfNeeded(_blocks.last.tag);
TextStyle parentStyle = _inlines.last.style;
_inlines.add(new _InlineElement(
tag,
style: parentStyle.merge(styleSheet.styles[tag]),
));
}
if (tag == 'a') {
_linkHandlers.add(delegate.createLink(element.attributes['href']));
}
return true;
}
@override
void visitElementAfter(md.Element element) {
final String tag = element.tag;
if (_isBlockTag(tag)) {
_addAnonymousBlockIfNeeded(styleSheet.styles[tag]);
final _BlockElement current = _blocks.removeLast();
Widget child;
if (current.children.isNotEmpty) {
child = new Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: current.children,
);
} else {
child = const SizedBox();
}
if (_isListTag(tag)) {
assert(_listIndents.isNotEmpty);
_listIndents.removeLast();
} else if (tag == 'li') {
if (_listIndents.isNotEmpty) {
child = new Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
new SizedBox(
width: styleSheet.listIndent,
child: _buildBullet(_listIndents.last),
),
new Expanded(child: child)
],
);
}
} else if (tag == 'blockquote') {
child = new DecoratedBox(
decoration: styleSheet.blockquoteDecoration,
child: new Padding(
padding: new EdgeInsets.all(styleSheet.blockquotePadding),
child: child,
),
);
} else if (tag == 'pre') {
child = new DecoratedBox(
decoration: styleSheet.codeblockDecoration,
child: new Padding(
padding: new EdgeInsets.all(styleSheet.codeblockPadding),
child: child,
),
);
} else if (tag == 'hr') {
child = new DecoratedBox(
decoration: styleSheet.horizontalRuleDecoration,
child: child,
);
}
_addBlockChild(child);
} else {
final _InlineElement current = _inlines.removeLast();
final _InlineElement parent = _inlines.last;
if (tag == 'img') {
// create an image widget for this image
current.children.add(_buildImage(element.attributes['src']));
} else if (tag == 'a') {
_linkHandlers.removeLast();
} else if (tag == 'demo') {
current.children.add(_buildGoDemos(element.attributes));
}
if (current.children.isNotEmpty) {
parent.children.addAll(current.children);
}
}
}
Widget _buildGoDemos(Map<String, dynamic> attrs) {
Widget targetGoDemos;
if (demoParser != null) {
targetGoDemos = demoParser(attrs);
}
return targetGoDemos ?? new Text('demo not exits');
}
Widget _buildImage(String src) {
final List<String> parts = src.split('#');
if (parts.isEmpty)
return const SizedBox();
final String path = parts.first;
double width;
double height;
if (parts.length == 2) {
final List<String> dimensions = parts.last.split('x');
if (dimensions.length == 2) {
width = double.parse(dimensions[0]);
height = double.parse(dimensions[1]);
}
}
Uri uri = Uri.parse(path);
Widget child;
if (uri.scheme == 'http' || uri.scheme == 'https') {
child = new Image.network(uri.toString(), width: width, height: height);
} else if (uri.scheme == 'data') {
child = _handleDataSchemeUri(uri, width, height);
} else if (uri.scheme == "resource") {
child = new Image.asset(path.substring(9), width: width, height: height);
} else {
String filePath = (imageDirectory == null
? uri.toFilePath()
: p.join(imageDirectory.path, uri.toFilePath()));
child = new Image.file(new File(filePath), width: width, height: height);
}
if (_linkHandlers.isNotEmpty) {
TapGestureRecognizer recognizer = _linkHandlers.last;
return new GestureDetector(child: child, onTap: recognizer.onTap);
} else {
return child;
}
}
Widget _handleDataSchemeUri(Uri uri, final double width, final double height) {
final String mimeType = uri.data.mimeType;
if (mimeType.startsWith('image/')) {
return new Image.memory(uri.data.contentAsBytes(), width: width, height: height);
} else if (mimeType.startsWith('text/')) {
return new Text(uri.data.contentAsString());
}
return const SizedBox();
}
Widget _buildBullet(String listTag) {
if (listTag == 'ul')
return new Text('', textAlign: TextAlign.center, style: styleSheet.styles['li']);
final int index = _blocks.last.nextListIndex;
return new Padding(
padding: const EdgeInsets.only(right: 5.0),
child: new Text('${index + 1}.', textAlign: TextAlign.right, style: styleSheet.styles['li']),
);
}
void _addParentInlineIfNeeded(String tag) {
if (_inlines.isEmpty) {
_inlines.add(new _InlineElement(
tag,
style: styleSheet.styles[tag],
));
}
}
void _addBlockChild(Widget child) {
final _BlockElement parent = _blocks.last;
if (parent.children.isNotEmpty)
parent.children.add(new SizedBox(height: styleSheet.blockSpacing));
parent.children.add(child);
parent.nextListIndex += 1;
}
void _addAnonymousBlockIfNeeded(TextStyle style) {
if (_inlines.isEmpty) {
return;
}
final _InlineElement inline = _inlines.single;
if (inline.children.isNotEmpty) {
List<Widget> mergedInlines = _mergeInlineChildren(inline);
final Wrap wrap = new Wrap(children: mergedInlines);
_addBlockChild(wrap);
_inlines.clear();
}
}
/// Merges adjacent [TextSpan] children of the given [_InlineElement]
List<Widget> _mergeInlineChildren(_InlineElement inline) {
List<Widget> mergedTexts = <Widget>[];
for (Widget child in inline.children) {
if (mergedTexts.isNotEmpty && mergedTexts.last is RichText && child is RichText) {
RichText previous = mergedTexts.removeLast();
List<TextSpan> children = previous.text.children != null
? new List.from(previous.text.children)
: [previous.text];
children.add(child.text);
TextSpan mergedSpan = new TextSpan(children: children);
mergedTexts.add(new RichText(
textScaleFactor: styleSheet.textScaleFactor,
text: mergedSpan,
));
} else {
mergedTexts.add(child);
}
}
return mergedTexts;
}
}

View File

@ -0,0 +1,307 @@
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:flutter/material.dart';
/// Defines which [TextStyle] objects to use for which Markdown elements.
class MarkdownStyleSheet {
/// Creates an explicit mapping of [TextStyle] objects to Markdown elements.
MarkdownStyleSheet({
this.a,
this.p,
this.code,
this.h1,
this.h2,
this.h3,
this.h4,
this.h5,
this.h6,
this.em,
this.strong,
this.blockquote,
this.img,
this.blockSpacing,
this.listIndent,
this.blockquotePadding,
this.blockquoteDecoration,
this.codeblockPadding,
this.codeblockDecoration,
this.horizontalRuleDecoration,
this.textScaleFactor = 1.0
}) : _styles = <String, TextStyle>{
'a': a,
'p': p,
'li': p,
'code': code,
'pre': p,
'h1': h1,
'h2': h2,
'h3': h3,
'h4': h4,
'h5': h5,
'h6': h6,
'em': em,
'strong': strong,
'blockquote': blockquote,
'img': img,
};
/// Creates a [MarkdownStyleSheet] from the [TextStyle]s in the provided [ThemeData].
factory MarkdownStyleSheet.fromTheme(ThemeData theme) {
assert(theme?.textTheme?.body1?.fontSize != null);
return new MarkdownStyleSheet(
a: const TextStyle(color: Colors.blue),
p: theme.textTheme.body1,
code: new TextStyle(
color: Colors.grey.shade700,
fontFamily: "monospace",
fontSize: theme.textTheme.body1.fontSize * 0.85
),
h1: theme.textTheme.headline,
h2: theme.textTheme.title,
h3: theme.textTheme.subhead,
h4: theme.textTheme.body2,
h5: theme.textTheme.body2,
h6: theme.textTheme.body2,
em: const TextStyle(fontStyle: FontStyle.italic),
strong: const TextStyle(fontWeight: FontWeight.bold),
blockquote: theme.textTheme.body1,
img: theme.textTheme.body1,
blockSpacing: 8.0,
listIndent: 32.0,
blockquotePadding: 8.0,
blockquoteDecoration: new BoxDecoration(
color: Colors.blue.shade100,
borderRadius: new BorderRadius.circular(2.0)
),
codeblockPadding: 8.0,
codeblockDecoration: new BoxDecoration(
color: Colors.grey.shade100,
borderRadius: new BorderRadius.circular(2.0)
),
horizontalRuleDecoration: new BoxDecoration(
border: new Border(
top: new BorderSide(width: 5.0, color: Colors.grey.shade300)
),
),
);
}
/// Creates a [MarkdownStyle] from the [TextStyle]s in the provided [ThemeData].
///
/// This constructor uses larger fonts for the headings than in
/// [MarkdownStyle.fromTheme].
factory MarkdownStyleSheet.largeFromTheme(ThemeData theme) {
return new MarkdownStyleSheet(
a: const TextStyle(color: Colors.blue),
p: theme.textTheme.body1,
code: new TextStyle(
color: Colors.grey.shade700,
fontFamily: "monospace",
fontSize: theme.textTheme.body1.fontSize * 0.85
),
h1: theme.textTheme.display3,
h2: theme.textTheme.display2,
h3: theme.textTheme.display1,
h4: theme.textTheme.headline,
h5: theme.textTheme.title,
h6: theme.textTheme.subhead,
em: const TextStyle(fontStyle: FontStyle.italic),
strong: const TextStyle(fontWeight: FontWeight.bold),
blockquote: theme.textTheme.body1,
img: theme.textTheme.body1,
blockSpacing: 8.0,
listIndent: 32.0,
blockquotePadding: 8.0,
blockquoteDecoration: new BoxDecoration(
color: Colors.blue.shade100,
borderRadius: new BorderRadius.circular(2.0)
),
codeblockPadding: 8.0,
codeblockDecoration: new BoxDecoration(
color: Colors.grey.shade100,
borderRadius: new BorderRadius.circular(2.0)
),
horizontalRuleDecoration: new BoxDecoration(
border: new Border(
top: new BorderSide(width: 5.0, color: Colors.grey.shade300)
),
),
);
}
/// Creates a new [MarkdownStyleSheet] based on the current style, with the
/// provided parameters overridden.
MarkdownStyleSheet copyWith({
TextStyle a,
TextStyle p,
TextStyle code,
TextStyle h1,
TextStyle h2,
TextStyle h3,
TextStyle h4,
TextStyle h5,
TextStyle h6,
TextStyle em,
TextStyle strong,
TextStyle blockquote,
TextStyle img,
double blockSpacing,
double listIndent,
double blockquotePadding,
Decoration blockquoteDecoration,
double codeblockPadding,
Decoration codeblockDecoration,
Decoration horizontalRuleDecoration,
double textScaleFactor,
}) {
return new MarkdownStyleSheet(
a: a ?? this.a,
p: p ?? this.p,
code: code ?? this.code,
h1: h1 ?? this.h1,
h2: h2 ?? this.h2,
h3: h3 ?? this.h3,
h4: h4 ?? this.h4,
h5: h5 ?? this.h5,
h6: h6 ?? this.h6,
em: em ?? this.em,
strong: strong ?? this.strong,
blockquote: blockquote ?? this.blockquote,
img: img ?? this.img,
blockSpacing: blockSpacing ?? this.blockSpacing,
listIndent: listIndent ?? this.listIndent,
blockquotePadding: blockquotePadding ?? this.blockquotePadding,
blockquoteDecoration: blockquoteDecoration ?? this.blockquoteDecoration,
codeblockPadding: codeblockPadding ?? this.codeblockPadding,
codeblockDecoration: codeblockDecoration ?? this.codeblockDecoration,
horizontalRuleDecoration: horizontalRuleDecoration ?? this.horizontalRuleDecoration,
textScaleFactor : textScaleFactor ?? this.textScaleFactor,
);
}
/// The [TextStyle] to use for `a` elements.
final TextStyle a;
/// The [TextStyle] to use for `p` elements.
final TextStyle p;
/// The [TextStyle] to use for `code` elements.
final TextStyle code;
/// The [TextStyle] to use for `h1` elements.
final TextStyle h1;
/// The [TextStyle] to use for `h2` elements.
final TextStyle h2;
/// The [TextStyle] to use for `h3` elements.
final TextStyle h3;
/// The [TextStyle] to use for `h4` elements.
final TextStyle h4;
/// The [TextStyle] to use for `h5` elements.
final TextStyle h5;
/// The [TextStyle] to use for `h6` elements.
final TextStyle h6;
/// The [TextStyle] to use for `em` elements.
final TextStyle em;
/// The [TextStyle] to use for `strong` elements.
final TextStyle strong;
/// The [TextStyle] to use for `blockquote` elements.
final TextStyle blockquote;
/// The [TextStyle] to use for `img` elements.
final TextStyle img;
/// The amount of vertical space to use between block-level elements.
final double blockSpacing;
/// The amount of horizontal space to indent list items.
final double listIndent;
/// The padding to use for `blockquote` elements.
final double blockquotePadding;
/// The decoration to use behind `blockquote` elements.
final Decoration blockquoteDecoration;
/// The padding to use for `pre` elements.
final double codeblockPadding;
/// The decoration to use behind for `pre` elements.
final Decoration codeblockDecoration;
/// The decoration to use for `hr` elements.
final Decoration horizontalRuleDecoration;
// The text scale factor to use in textual elements
final double textScaleFactor;
/// A [Map] from element name to the corresponding [TextStyle] object.
Map<String, TextStyle> get styles => _styles;
Map<String, TextStyle> _styles;
@override
bool operator ==(dynamic other) {
if (identical(this, other))
return true;
if (other.runtimeType != MarkdownStyleSheet)
return false;
final MarkdownStyleSheet typedOther = other;
return typedOther.a == a
&& typedOther.p == p
&& typedOther.code == code
&& typedOther.h1 == h1
&& typedOther.h2 == h2
&& typedOther.h3 == h3
&& typedOther.h4 == h4
&& typedOther.h5 == h5
&& typedOther.h6 == h6
&& typedOther.em == em
&& typedOther.strong == strong
&& typedOther.blockquote == blockquote
&& typedOther.img == img
&& typedOther.blockSpacing == blockSpacing
&& typedOther.listIndent == listIndent
&& typedOther.blockquotePadding == blockquotePadding
&& typedOther.blockquoteDecoration == blockquoteDecoration
&& typedOther.codeblockPadding == codeblockPadding
&& typedOther.codeblockDecoration == codeblockDecoration
&& typedOther.horizontalRuleDecoration == horizontalRuleDecoration
&& typedOther.textScaleFactor == textScaleFactor;
}
@override
int get hashCode {
return hashList([
a,
p,
code,
h1,
h2,
h3,
h4,
h5,
h6,
em,
strong,
blockquote,
img,
blockSpacing,
listIndent,
blockquotePadding,
blockquoteDecoration,
codeblockPadding,
codeblockDecoration,
horizontalRuleDecoration,
textScaleFactor,
]);
}
}

View File

@ -0,0 +1,247 @@
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:io';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:markdown/markdown.dart' as md;
import 'package:meta/meta.dart';
import 'builder.dart';
import 'style_sheet.dart';
//
typedef Widget ItemDemoBuilder(Map<String, dynamic> attrs);
/// Signature for callbacks used by [MarkdownWidget] when the user taps a link.
///
/// Used by [MarkdownWidget.onTapLink].
typedef void MarkdownTapLinkCallback(String href);
/// Creates a format [TextSpan] given a string.
///
/// Used by [MarkdownWidget] to highlight the contents of `pre` elements.
abstract class SyntaxHighlighter { // ignore: one_member_abstracts
/// Returns the formated [TextSpan] for the given string.
TextSpan format(String source);
}
/// A base class for widgets that parse and display Markdown.
///
/// Supports all standard Markdown from the original
/// [Markdown specification](https://daringfireball.net/projects/markdown/).
///
/// See also:
///
/// * [Markdown], which is a scrolling container of Markdown.
/// * [MarkdownBody], which is a non-scrolling container of Markdown.
/// * <https://daringfireball.net/projects/markdown/>
abstract class MarkdownWidget extends StatefulWidget {
/// Creates a widget that parses and displays Markdown.
///
/// The [data] argument must not be null.
const MarkdownWidget({
Key key,
@required this.data,
this.styleSheet,
this.syntaxHighlighter,
this.onTapLink,
this.imageDirectory,
this.demoBuilder,
}) : assert(data != null),
super(key: key);
/// The Markdown to display.
final String data;
/// The styles to use when displaying the Markdown.
///
/// If null, the styles are inferred from the current [Theme].
final MarkdownStyleSheet styleSheet;
/// The syntax highlighter used to color text in `pre` elements.
///
/// If null, the [MarkdownStyleSheet.code] style is used for `pre` elements.
final SyntaxHighlighter syntaxHighlighter;
/// Called when the user taps a link.
final MarkdownTapLinkCallback onTapLink;
/// The base directory holding images referenced by Img tags with local file paths.
final Directory imageDirectory;
final ItemDemoBuilder demoBuilder;
/// Subclasses should override this function to display the given children,
/// which are the parsed representation of [data].
@protected
Widget build(BuildContext context, List<Widget> children);
@override
_MarkdownWidgetState createState() => new _MarkdownWidgetState();
}
class DemosSyntax extends md.InlineSyntax {
DemosSyntax() : super('\\[demo:([a-z0-9_+-]+)\\]');
bool onMatch(parser, match) {
var anchor = new md.Element.empty('demo');
anchor.attributes['id'] = match[1];
parser.addNode(anchor);
return true;
}
}
class _MarkdownWidgetState extends State<MarkdownWidget> implements MarkdownBuilderDelegate {
List<Widget> _children;
final List<GestureRecognizer> _recognizers = <GestureRecognizer>[];
@override
void didChangeDependencies() {
_parseMarkdown();
super.didChangeDependencies();
}
@override
void didUpdateWidget(MarkdownWidget oldWidget) {
super.didUpdateWidget(oldWidget);
if (widget.data != oldWidget.data
|| widget.styleSheet != oldWidget.styleSheet)
_parseMarkdown();
}
@override
void dispose() {
_disposeRecognizers();
super.dispose();
}
void _parseMarkdown() {
final MarkdownStyleSheet styleSheet = widget.styleSheet ?? new MarkdownStyleSheet.fromTheme(Theme.of(context));
_disposeRecognizers();
// TODO: This can be optimized by doing the split and removing \r at the same time
final List<String> lines = widget.data.replaceAll('\r\n', '\n').split('\n');
final md.ExtensionSet extens = new md.ExtensionSet([
md.FencedCodeBlockSyntax()
], [
new DemosSyntax(),
new md.InlineHtmlSyntax(),
]);
final md.Document document = new md.Document(encodeHtml: false, extensionSet: extens);
final MarkdownBuilder builder = new MarkdownBuilder(
delegate: this,
styleSheet: styleSheet,
imageDirectory: widget.imageDirectory,
demoParser: widget.demoBuilder
);
_children = builder.build(document.parseLines(lines));
}
void _disposeRecognizers() {
if (_recognizers.isEmpty)
return;
final List<GestureRecognizer> localRecognizers = new List<GestureRecognizer>.from(_recognizers);
_recognizers.clear();
for (GestureRecognizer recognizer in localRecognizers)
recognizer.dispose();
}
@override
GestureRecognizer createLink(String href) {
final TapGestureRecognizer recognizer = new TapGestureRecognizer()
..onTap = () {
if (widget.onTapLink != null)
widget.onTapLink(href);
};
_recognizers.add(recognizer);
return recognizer;
}
@override
TextSpan formatText(MarkdownStyleSheet styleSheet, String code) {
if (widget.syntaxHighlighter != null)
return widget.syntaxHighlighter.format(code);
return new TextSpan(style: styleSheet.code, text: code);
}
@override
Widget build(BuildContext context) => widget.build(context, _children);
}
/// A non-scrolling widget that parses and displays Markdown.
///
/// Supports all standard Markdown from the original
/// [Markdown specification](https://daringfireball.net/projects/markdown/).
///
/// See also:
///
/// * [Markdown], which is a scrolling container of Markdown.
/// * <https://daringfireball.net/projects/markdown/>
class MarkdownBody extends MarkdownWidget {
/// Creates a non-scrolling widget that parses and displays Markdown.
const MarkdownBody({
Key key,
String data,
MarkdownStyleSheet styleSheet,
SyntaxHighlighter syntaxHighlighter,
MarkdownTapLinkCallback onTapLink,
Directory imageDirectory,
ItemDemoBuilder demoBuilder,
}) : super(
key: key,
data: data,
styleSheet: styleSheet,
syntaxHighlighter: syntaxHighlighter,
onTapLink: onTapLink,
imageDirectory: imageDirectory,
demoBuilder: demoBuilder
);
@override
Widget build(BuildContext context, List<Widget> children) {
if (children.length == 1)
return children.single;
return new Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: children,
);
}
}
/// A scrolling widget that parses and displays Markdown.
///
/// Supports all standard Markdown from the original
/// [Markdown specification](https://daringfireball.net/projects/markdown/).
///
/// See also:
///
/// * [MarkdownBody], which is a non-scrolling container of Markdown.
/// * <https://daringfireball.net/projects/markdown/>
class Markdown extends MarkdownWidget {
/// Creates a scrolling widget that parses and displays Markdown.
const Markdown({
Key key,
String data,
MarkdownStyleSheet styleSheet,
SyntaxHighlighter syntaxHighlighter,
MarkdownTapLinkCallback onTapLink,
Directory imageDirectory,
this.padding: const EdgeInsets.all(16.0),
}) : super(
key: key,
data: data,
styleSheet: styleSheet,
syntaxHighlighter: syntaxHighlighter,
onTapLink: onTapLink,
imageDirectory: imageDirectory,
);
/// The amount of space by which to inset the children.
final EdgeInsets padding;
@override
Widget build(BuildContext context, List<Widget> children) {
return new ListView(padding: padding, children: children);
}
}

View File

@ -1,4 +1,4 @@
import 'package:flutter_markdown/flutter_markdown.dart' as md;
import '../components/flutter_markdown/lib/flutter_markdown.dart' as md;
import 'package:flutter/material.dart';
import 'package:flutter_go/utils/high_light_code.dart';

View File

@ -4,6 +4,7 @@
import 'dart:core';
import 'package:flutter/material.dart';
import 'package:flutter_go/utils/data_utils.dart';
import '../routers/application.dart';
import '../routers/routers.dart';
@ -37,7 +38,7 @@ class _WidgetDemoState extends State<WidgetDemo> {
CollectionControlModel _collectionControl = new CollectionControlModel();
var _collectionIcons;
List widgetDemosList = new WidgetDemoList().getDemos();
String _router = '';
String widgetType = 'old';
final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
List<Widget> _buildContent() {
@ -64,57 +65,67 @@ class _WidgetDemoState extends State<WidgetDemo> {
@override
void initState() {
super.initState();
_collectionControl.getRouterByName(widget.title).then((list) {
widgetDemosList.forEach((item) {
if (item.name == widget.title) {
_router = item.routerName;
// 这里不能直接 使用 ` ModalRoute.of(context)` 会产生报错
Future.delayed(Duration.zero, () {
String currentPath = ModalRoute.of(context).settings.name;
if (currentPath.indexOf('/standard-page') == 0) {
widgetType = 'standard';
}
Map<String, String> params = {
'type': widgetType,
"url": currentPath,
"name": widget.title
};
DataUtils.checkCollected(params).then((result) {
if (this.mounted) {
setState(() {
_hasCollected = result;
});
}
});
if (this.mounted) {
setState(() {
_hasCollected = list.length > 0;
});
}
});
}
// 点击收藏按钮
_getCollection() {
String currentRouterPath = ModalRoute.of(context).settings.name;
Map<String, String> params = {
"type": widgetType,
"url": currentRouterPath,
"name": widget.title
};
if (_hasCollected) {
// 删除操作
_collectionControl.deleteByName(widget.title).then((result) {
if (result > 0 && this.mounted) {
setState(() {
_hasCollected = false;
});
DataUtils.removeCollected(params, context).then((result) {
if (result) {
_scaffoldKey.currentState
.showSnackBar(SnackBar(content: Text('已取消收藏')));
if (ApplicationEvent.event != null) {
ApplicationEvent.event
.fire(CollectionEvent(widget.title, _router, true));
.fire(CollectionEvent(widget.title, currentRouterPath, true));
}
if (this.mounted) {
setState(() {
_hasCollected = false;
});
}
return;
}
print('删除错误');
});
} else {
// 插入操作
_collectionControl
.insert(Collection(name: widget.title, router: _router))
.then((result) {
if (this.mounted) {
setState(() {
_hasCollected = true;
});
if (ApplicationEvent.event != null) {
ApplicationEvent.event
.fire(CollectionEvent(widget.title, _router, false));
DataUtils.addCollected(params, context).then((result) {
if (result) {
if (this.mounted) {
setState(() {
_hasCollected = true;
});
}
_scaffoldKey.currentState
.showSnackBar(SnackBar(content: Text('收藏成功')));
if (ApplicationEvent.event != null) {
ApplicationEvent.event
.fire(CollectionEvent(widget.title, currentRouterPath, false));
}
}
});
}
@ -158,28 +169,28 @@ class _WidgetDemoState extends State<WidgetDemo> {
PopupMenuButton<String>(
onSelected: _selectValue,
itemBuilder: (BuildContext context) => <PopupMenuEntry<String>>[
const PopupMenuItem<String>(
value: 'doc',
child: ListTile(
leading: Icon(
Icons.library_books,
size: 22.0,
),
title: Text('查看文档'),
),
const PopupMenuItem<String>(
value: 'doc',
child: ListTile(
leading: Icon(
Icons.library_books,
size: 22.0,
),
const PopupMenuDivider(),
const PopupMenuItem<String>(
value: 'code',
child: ListTile(
leading: Icon(
Icons.code,
size: 22.0,
),
title: Text('查看Demo'),
),
title: Text('查看文档'),
),
),
const PopupMenuDivider(),
const PopupMenuItem<String>(
value: 'code',
child: ListTile(
leading: Icon(
Icons.code,
size: 22.0,
),
],
title: Text('查看Demo'),
),
),
],
),
],
),

View File

@ -3,56 +3,89 @@ import 'package:fluro/fluro.dart';
import './widget_item.dart';
import '../routers/application.dart';
import '../widgets/index.dart';
import '../model/widget.dart';
class WidgetItemContainer extends StatelessWidget {
final int columnCount; //一行几个
final List<dynamic> categories;
final bool isWidgetPoint;
final List<CommonItem> commonItems;
// final bool isWidgetPoint;
// 所有的可用demos;
final List widgetDemosList = new WidgetDemoList().getDemos();
WidgetItemContainer(
{Key key,
@required this.categories,
@required this.commonItems,
@required this.columnCount,
@required this.isWidgetPoint})
// @required this.isWidgetPoint
})
: super(key: key);
/// 跳转goup
void tapToGroup(CategoryComponent cate, BuildContext context) {
Application.router
.navigateTo(context, "/category/${cate.token}", transition: TransitionType.inFromRight);
}
/// 跳转到老的widget界面
void tapToOldWidget(WidgetLeaf leaf, BuildContext context) {
String targetName = leaf.name;
String targetRouter = '/category/error/404';
widgetDemosList.forEach((item) {
if (item.name == targetName) {
targetRouter = item.routerName;
}
});
Application.router.navigateTo(context, targetRouter, transition: TransitionType.inFromRight);
}
/// 跳转到新的标准页
void tapToStandardPage(WidgetLeaf leaf, BuildContext context) {
String targetRouter = '/standard-page/${leaf.pageId}';
Application.router.navigateTo(context, targetRouter, transition: TransitionType.inFromRight);
}
List<Widget> _buildColumns(context) {
List<Widget> _listWidget = [];
List<Widget> _listRows = [];
int addI;
for (int i = 0, length = categories.length; i < length; i += columnCount) {
for (int i = 0, length = commonItems.length; i < length; i += columnCount) {
_listRows = [];
for (int innerI = 0; innerI < columnCount; innerI++) {
addI = innerI + i;
if (addI < length) {
dynamic item = categories[addI];
CommonItem item = commonItems[addI];
_listRows.add(
Expanded(
flex: 1,
child: WidgetItem(
title: item.name,
onTap: () {
if (isWidgetPoint) {
String targetName = item.name;
String targetRouter = '/category/error/404';
widgetDemosList.forEach((item) {
if (item.name == targetName) {
targetRouter = item.routerName;
}
});
Application.router.navigateTo(context, "$targetRouter", transition: TransitionType.inFromRight);
} else {
Application.router
.navigateTo(context, "/category/${item.name}", transition: TransitionType.inFromRight);
String type = item.type;
if (type == "category") {
return tapToGroup(item as CategoryComponent, context);
}
if (type == "widget") {
WidgetLeaf leaf = item as WidgetLeaf;
if (leaf.display == "standard") {
return tapToStandardPage(leaf, context);
} else {
return tapToOldWidget(leaf, context);
}
}
Application.router
.navigateTo(context, "/category/error/404", transition: TransitionType.inFromRight);
},
index: addI,
totalCount: length,
rowLength: columnCount,
textSize: isWidgetPoint ? 'middle' : 'small',
textSize: true ? 'middle' : 'small',
),
),
);
@ -81,3 +114,4 @@ class WidgetItemContainer extends StatelessWidget {
);
}
}

View File

@ -16,6 +16,7 @@ import 'package:flutter_jpush/flutter_jpush.dart';
import 'package:flutter_go/event/event_bus.dart';
import 'package:flutter_go/event/event_model.dart';
import 'package:event_bus/event_bus.dart';
import 'package:flutter_go/model/widget.dart';
//import 'views/welcome_page/index.dart';
@ -25,9 +26,9 @@ var db;
class MyApp extends StatefulWidget {
MyApp() {
final router = new Router();
Routes.configureRoutes(router);
// 这里设置项目环境
Application.env = ENV.PRODUCTION;
Application.router = router;
}
@ -128,7 +129,6 @@ class _MyAppState extends State<MyApp> {
});
print('身份信息验证失败:$onError');
});
ApplicationEvent.event.on<UserSettingThemeColorEvent>().listen((event) {
print('接收到的 event $event');
});
@ -154,8 +154,9 @@ class _MyAppState extends State<MyApp> {
@override
Widget build(BuildContext context) {
// WidgetTree.getCommonItemByPath([15, 17], Application.widgetTree);
return new MaterialApp(
title: 'title',
title: 'titles',
theme: new ThemeData(
primaryColor: Color(this.themeColor),
backgroundColor: Color(0xFFEFEFEF),
@ -188,6 +189,10 @@ void main() async {
await provider.init(true);
sp = await SpUtil.getInstance();
new SearchHistoryList(sp);
await DataUtils.getWidgetTreeList().then((List json) {
if (json == null) return;
Application.widgetTree = WidgetTree.buildWidgetTree(json);
});
db = Provider.db;
runApp(new MyApp());
}

View File

@ -49,15 +49,21 @@ class CollectionControlModel {
List list = await sql.getByCondition();
List<Collection> resultList = [];
list.forEach((item){
print(item);
print('collection item =>> $item');
resultList.add(Collection.fromJSON(item));
});
return resultList;
}
// 通过收藏名获取router
Future getRouterByName(String name) async {
List list = await sql.getByCondition(conditions: {'name': name});
/// 通过收藏名获取router
/// 因为名称很容易重复. 所以这里使用path router做唯一判断
// Future getRouterByName(String name) async {
// List list = await sql.getByCondition(conditions: {'name': name});
// return list;
// }
Future getRouterByUrl(String path) async {
List list = await sql.getByCondition(conditions: {'router': path});
return list;
}
@ -65,4 +71,8 @@ class CollectionControlModel {
Future deleteByName(String name) async{
return await sql.delete(name,'name');
}
// 通过path删除
Future deleteByPath(String path) async{
return await sql.delete(path,'router');
}
}

View File

@ -5,6 +5,13 @@ import "package:flutter/material.dart";
import 'package:flutter_go/utils/sql.dart';
enum treeNode {
CategoryComponent,
WidgetLeaf
}
//typedef aaa
abstract class WidgetInterface {
int get id;
@ -142,3 +149,182 @@ class WidgetControlModel {
return widgets;
}
}
// 抽象类
abstract class CommonItem<T> {
int id;
String name;
int parentId;
String type;
List<CommonItem> children;
String token;
/// 父级节点, 存放整个CommonItem对象node = ! null
///
CommonItem parent;
String toString() {
return "CommonItem {name: $name, type: $type, parentId: $parentId, token: $token, children长度 ${children}";
}
T getChild(String token);
T addChildren(Object item);
// 从children树中. 查找任意子节点
T find(String token, [CommonItem node]);
}
// tree的group树干
class CategoryComponent extends CommonItem {
int id;
String name;
int parentId;
CommonItem parent;
String token;
List<CommonItem> children = [];
String type = 'category';
CategoryComponent({
@required this.id,
@required this.name,
@required this.parentId,
this.children,
this.parent
});
CategoryComponent.fromJson(Map json) {
this.id = int.parse(json['id']);
this.name = json['name'];
this.parentId = json['parentId'];
this.token = json['id'] + json['type'];
}
void addChildren(Object item) {
if (item is CategoryComponent) {
CategoryComponent cate = item;
cate.parent = this;
this.children.add(
cate
);
}
if (item is WidgetLeaf) {
WidgetLeaf widget = item;
widget.parent = this;
this.children.add(
widget
);
}
}
@override
CommonItem getChild(String token) {
return children.firstWhere((CommonItem item) => item.token == token, orElse: () => null);
}
@override
CommonItem find(String token, [CommonItem node]) {
CommonItem ret;
if (node !=null) {
if (node.token == token) {
return node;
} else {
// 循环到叶子节点, 返回 空
if (node.children == null) {
return null;
}
for (int i = 0; i < node.children.length; i++) {
CommonItem temp = this.find(token, node.children[i]);
if (temp != null) {
ret = temp;
}
}
}
} else {
ret = find(token, this);
}
return ret;
}
}
// 叶子节点
class WidgetLeaf extends CommonItem {
int id;
String name;
int parentId;
String display; // 展示类型, 区分老的widget文件下的详情
String author; // 文档负责人
String path; // 路由地址
String pageId; // 界面ID
CommonItem parent;
String type = 'widget';
WidgetLeaf({
@required this.id,
@required this.name,
@required this.display,
this.author,
this.path,
this.pageId
});
WidgetLeaf.fromJson(Map json) {
this.id = int.parse(json['id']);
this.name = json['name'];
this.display = json['display'];
this.author = json['author'] ?? null;
this.path = json['path'] ?? null;
this.pageId = json['pageId'] ?? null;
this.token = json['id'] + json['type'];
}
@override
CommonItem getChild(String token) {
return null;
}
@override
addChildren(Object item) {
// TODO: implement addChildren
return null;
}
CommonItem find(String token, [CommonItem node]){
return null;
}
}
class WidgetTree {
static CategoryComponent buildWidgetTree(List json, [parent]){
CategoryComponent current;
if (parent != null) {
current = parent;
} else {
current = CategoryComponent(id: 0, name: 'root', parentId: null, children: []);
}
json.forEach((item) {
// 归属分类级别
if (['root', 'category'].indexOf(item['type']) != -1) {
CategoryComponent cate = CategoryComponent.fromJson(item);
if (cate.children != null) {
buildWidgetTree(item['children'], cate);
}
current.addChildren(cate);
} else {
// 归属最后一层叶子节点
WidgetLeaf cate = WidgetLeaf.fromJson(item);
current.addChildren(cate);
}
});
return current;
}
static CategoryComponent getCommonItemById(List<int> path, CategoryComponent root) {
print("getCommonItemByPath $path");
print("root $root");
CommonItem childLeaf;
int first = path.first;
path = path.sublist(1);
print("path:::: $path");
if (path.length >= 0) {
// childLeaf = root.getChild(path.first);
}
return childLeaf;
}
}

View File

@ -0,0 +1 @@
[{"name":"demoName","screenShot":"","author":"yourName","email":"yourEmail","desc":"这是一个测试的标准demo","id":"1a29aa8e_32ae_4241_9c8a_5c9e1f92b096"}]

View File

@ -0,0 +1,9 @@
{
"name": "demoName",
"screenShot": "",
"author":"yourName",
"email": "yourEmail",
"desc": "这是一个测试的标准demo",
"id": "1a29aa8e_32ae_4241_9c8a_5c9e1f92b096"
}

View File

@ -0,0 +1,15 @@
//
// Created with flutter go cli
// User: yourName
// Time: 2019-06-10 20:37:27.289097
// email: yourEmail
// desc: 这是一个测试的标准demo
//
import 'src/index.dart';
var demoWidgets = [
new Demo()
];

View File

@ -0,0 +1,16 @@
import 'package:flutter/material.dart';
class Demo extends StatefulWidget {
@override
_State createState() => _State();
}
class _State extends State<Demo> {
@override
Widget build(BuildContext context) {
return Container(
child: RaisedButton(onPressed: () {}, child: Text('以下方式引入的demo'))
);
}
}

View File

@ -0,0 +1,4 @@
import 'demoName_yourName_1a29aa8e_32ae_4241_9c8a_5c9e1f92b096/index.dart' as StandardDemo_demoName_1a29aa8e_32ae_4241_9c8a_5c9e1f92b096;
var demoObjects = {
'1a29aa8e_32ae_4241_9c8a_5c9e1f92b096': StandardDemo_demoName_1a29aa8e_32ae_4241_9c8a_5c9e1f92b096.demoWidgets
};

View File

@ -0,0 +1,17 @@
{
"70c429df-c27d-4843-8e28-1e6885c9276b": {
"name": "button-red",
"screenShot": "",
"author": "sanfan",
"email": "sanfan.hx@alibaba-inc.com",
"desc": "desc",
}
}

View File

@ -0,0 +1,20 @@
# 目录说明
本目录文件结构与文件命名, 使用cli进行更新
# demos 目录文件结构
```
demos
├── ${demoName}-${author}-${32位demoID}
│   ├── index.dart
│   └── src
│   ├── .demo.json
│   └── ${demoName}.dart
├── ...${demoName}-${author}-${32位demoID}
├── index.dart
├── info.json
└── readme.md
```

View File

@ -1,9 +1,11 @@
import 'package:flutter/material.dart';
class WidgetName2Icon {
static Map<String,dynamic> icons = {
"Developer": Icons.developer_mode,
"Standard": Icons.pages ,
"Element":Icons.explicit,
"Components":Icons.extension,
"Themes":Icons.filter_b_and_w,
"Theme":Icons.filter_b_and_w,
"Form":Icons.table_chart,
"Frame":Icons.aspect_ratio,
"Media":Icons.subscriptions,

View File

@ -1,12 +1,20 @@
import 'package:flutter/material.dart';
import 'package:fluro/fluro.dart';
import 'package:flutter_go/utils/shared_preferences.dart';
import '../model/widget.dart';
enum ENV {
PRODUCTION,
DEV,
}
class Application {
/// 通过Application设计环境变量
static ENV env = ENV.PRODUCTION;
static Router router;
static TabController controller;
static SpUtil sharePeferences;
static SpUtil sharePeference;
static CategoryComponent widgetTree;
static Map<String, String> github = {
'widgetsURL':'https://github.com/alibaba/flutter-go/blob/develop/lib/widgets/',
@ -14,4 +22,15 @@ class Application {
//'master':'https://github.com/alibaba-paimai-frontend/flutter-common-widgets-app/tree/master/lib/widgets/'
};
/// 所有获取配置的唯一入口
Map<String, String> get config {
if (Application.env == ENV.PRODUCTION) {
return {};
}
if (Application.env == ENV.DEV) {
return {};
}
return {};
}
}

View File

@ -10,7 +10,7 @@ import 'package:flutter_go/views/login_page/login_page.dart';
import 'package:flutter_go/model/user_info.dart';
import 'package:flutter_go/views/collection_page/collection_page.dart';
import 'package:flutter_go/views/collection_page/collection_full_page.dart';
import 'package:flutter_go/views/standard_demo_page/index.dart';
// app的首页
var homeHandler = new Handler(
@ -35,9 +35,9 @@ var collectionHandler = new Handler(
var categoryHandler = new Handler(
handlerFunc: (BuildContext context, Map<String, List<String>> params) {
String name = params["type"]?.first;
String ids = params["ids"]?.first;
return new CategoryHome(name);
return new CategoryHome(ids);
},
);
@ -60,12 +60,15 @@ var fullScreenCodeDialog = new Handler(
var webViewPageHand = new Handler(
handlerFunc: (BuildContext context, Map<String, List<String>> params) {
String title = params['title']?.first;
String url = params['url']?.first;
return new WebViewPage(url, title);
});
String title = params['title']?.first;
String url = params['url']?.first;
return new WebViewPage(url, title);
});
// var issuesMessageHandler = new Handler(
// handlerFunc: (BuildContext context, Map<String, List<String>> params) {
// return issuesMessagePage();
// });
var standardPageHandler = new Handler(
handlerFunc: (BuildContext context, Map<String, List<String>> params) {
String id = params['id']?.first;
return StandardView(id: id);
}
);

View File

@ -2,10 +2,9 @@
import 'package:fluro/fluro.dart';
import 'package:flutter/material.dart';
import 'package:flutter_go/utils/analytics.dart' show analytics;
import '../widgets/index.dart';
import './router_handler.dart';
import '../standard_pages/index.dart';
class Routes {
static String root = "/";
static String home = "/home";
@ -16,6 +15,7 @@ class Routes {
static String issuesMessage='/issuesMessage';
static String collectionPage = '/collection-page';
static String collectionFullPage = '/collection-full-page';
static String standardPage = '/standard-page/:id';
static void configureRoutes(Router router) {
List widgetDemosList = new WidgetDemoList().getDemos();
@ -25,7 +25,7 @@ class Routes {
router.define(home, handler: homeHandler);
router.define(collectionPage,handler:collectionHandler);
router.define(collectionFullPage,handler:collectionFullHandler);
router.define('/category/:type', handler: categoryHandler);
router.define('/category/:ids', handler: categoryHandler);
router.define('/category/error/404', handler: widgetNotFoundHandler);
router.define(loginPage, handler: loginPageHandler);
router.define(codeView,handler:fullScreenCodeDialog);
@ -41,5 +41,10 @@ class Routes {
});
router.define('${demo.routerName}', handler: handler);
});
router.define(standardPage,handler:standardPageHandler);
// router.define(webViewPage,handler:webViewPageHand);
// standardPages.forEach((String id, String md) => {
//
// });
}
}

View File

@ -0,0 +1,11 @@
[
{
"name": "standard",
"screenShot": "",
"author": "sanfan",
"title": "介绍页",
"email": "hanxu317@qq.com",
"desc": "desc",
"id": "ee4feb8e_32ae_4241_9c8a_5c9e1f92b096"
}
]

View File

@ -0,0 +1,11 @@
import 'standard_sanfan_ee4feb8e_32ae_4241_9c8a_5c9e1f92b096/index.dart' as StandardPage_standard_ee4feb8e_32ae_4241_9c8a_5c9e1f92b096;
class StandardPages {
Map<String, String> standardPages;
Map<String, String> getPages() {
return {
'ee4feb8e_32ae_4241_9c8a_5c9e1f92b096': StandardPage_standard_ee4feb8e_32ae_4241_9c8a_5c9e1f92b096.getMd()
};
}
}

View File

@ -0,0 +1,10 @@
{
"name": "standard",
"screenShot": "",
"author":"sanfan",
"title":"介绍页",
"email": "hanxu317@qq.com",
"desc": "desc",
"id": "ee4feb8e_32ae_4241_9c8a_5c9e1f92b096"
}

View File

@ -0,0 +1,56 @@
String getMd() {
return """
# 标准的详情页
您可以在这个界面中, 编写大多数的markdown文案, 他会在 **goCli watch** 下同步被编译成 **dart** 文件
您可以通过goCli创建详情页所需要的demo
[demo:1a29aa8e_32ae_4241_9c8a_5c9e1f92b096]
```
goCLi createDemo
```
在flutter go 根文件下通过命令行输入以上命令可以进行以下操作
[✓] 请输入新增加的demo名称? demoName
[✓] 请输入您的姓名(使用英文) yourName
[✓] 请输入您的github的email地址 yourEmail
[✓] 请输入您demo的描述 这是一个测试的标准demo
在完成以上操作后, 可以得到这样的输出:
```
------------------
您新增的组件信息如下
==================
{
name : demoName
author : yourName
email : yourEmail
desc : 这是一个测试的标准demo
}
==================
[✓] Is this the config you want ? (Y/n) y
{
新建的demo文件位于 : /flutter go/lib/page_demo_package/demoName_yourName_1a29aa8e_32ae_4241_9c8a_5c9e1f92b096
demoId为 : 1a29aa8e_32ae_4241_9c8a_5c9e1f92b096
markdown中调用方式 : [demo:1a29aa8e_32ae_4241_9c8a_5c9e1f92b096]
}
```
您可以在任意详情页中, 通过以下方式调用
```
[demo: 1a29aa8e_32ae_4241_9c8a_5c9e1f92b096]
```""";
}

View File

@ -0,0 +1,50 @@
# 标准的详情页
您可以在这个界面中, 编写大多数的markdown文案, 他会在 **goCli watch** 下同步被编译成 **dart** 文件
您可以通过goCli创建详情页所需要的demo
[demo:1a29aa8e_32ae_4241_9c8a_5c9e1f92b096]
```
goCLi createDemo
```
在flutter go 根文件下通过命令行输入以上命令可以进行以下操作
[✓] 请输入新增加的demo名称? demoName
[✓] 请输入您的姓名(使用英文) yourName
[✓] 请输入您的github的email地址 yourEmail
[✓] 请输入您demo的描述 这是一个测试的标准demo
在完成以上操作后, 可以得到这样的输出:
```
------------------
您新增的组件信息如下
==================
{
name : demoName
author : yourName
email : yourEmail
desc : 这是一个测试的标准demo
}
==================
[✓] Is this the config you want ? (Y/n) y
{
新建的demo文件位于 : /flutter go/lib/page_demo_package/demoName_yourName_1a29aa8e_32ae_4241_9c8a_5c9e1f92b096
demoId为 : 1a29aa8e_32ae_4241_9c8a_5c9e1f92b096
markdown中调用方式 : [demo:1a29aa8e_32ae_4241_9c8a_5c9e1f92b096]
}
```
您可以在任意详情页中, 通过以下方式调用
```
[demo: 1a29aa8e_32ae_4241_9c8a_5c9e1f92b096]
```

View File

@ -1,7 +1,9 @@
import 'dart:async' show Future;
import 'package:fluro/fluro.dart';
import 'package:flutter_go/model/collection.dart';
import 'package:flutter_go/model/version.dart';
import 'package:flutter_go/model/widget.dart';
import 'package:package_info/package_info.dart';
import 'package:flutter_go/model/responseData.dart';
@ -39,7 +41,6 @@ class DataUtils {
var response = await NetUtils.get(Api.CHECK_LOGIN);
print('response: $response');
try {
print('1111');
if (response['success']) {
print('${response['success']} ${response['data']} response[succes]');
UserInformation userInfo = UserInformation.fromJson(response['data']);
@ -104,4 +105,97 @@ class DataUtils {
return false;
}
}
/// 获取widget列表处的树型数据
static Future<List> getWidgetTreeList() async {
try {
var response = await NetUtils.get(Api.GET_WIDGET_TREE);
print('组件树:$response');
if (response != null && response['success']) {
return response['data'];
} else {
return [];
}
} catch (error) {
print('获取组件树 error $error');
}
}
// 校验是否收藏
static Future<bool> checkCollected(Map<String, String> params) async {
print('url 地址:${Api.CHECK_COLLECTED} $params');
try {
var response = await NetUtils.post(Api.CHECK_COLLECTED, params);
return response != null && response['hasCollected'];
} catch (error) {
print('校验收藏 error $error');
}
}
// 添加收藏
static Future addCollected(Map<String, String> params, context) async {
var response = await NetUtils.post(Api.ADD_COLLECTION, params);
if (response['status'] == 401 && response['message'] == '请先登录') {
Application.router.navigateTo(context, '${Routes.loginPage}',
transition: TransitionType.nativeModal);
}
return response != null && response['success'];
}
// 移出收藏
static Future removeCollected(Map<String, String> params, context) async {
var response = await NetUtils.post(Api.REMOVE_COLLECTION, params);
if (response['status'] == 401 && response['message'] == '请先登录') {
Application.router.navigateTo(context, '${Routes.loginPage}',
transition: TransitionType.nativeModal);
}
return response != null && response['success'];
}
// 获取全部收藏
static Future getAllCollections(context) async {
var response = await NetUtils.get(Api.GET_ALL_COLLECTION);
List<Collection> responseList = [];
if (response['status'] == 401 && response['message'] == '请先登录') {
Application.router.navigateTo(context, '${Routes.loginPage}',
transition: TransitionType.nativeModal);
}
if (response != null && response['success'] == true) {
for (int i = 0; i < response['data'].length; i++) {
Map<String, dynamic> tempCo = response['data'][i];
responseList.add(Collection.fromJSON(
{"name": tempCo['name'], "router": tempCo['url']}));
}
return responseList;
} else {
return [];
}
}
// 搜索组件
static Future searchWidget(String name) async {
var response = await NetUtils.get(Api.SEARCH_WIDGET, {"name": name});
List<WidgetPoint> list = [];
if (response != null && response['success'] == true) {
for (int i = 0; i < response['data'].length; i++) {
var json = response['data'][i];
String routerName;
if (json['display'] == 'old') {
routerName = json['path'];
} else {
routerName = json['pageId'];
}
Map<String, dynamic> tempMap = {
"name": json['name'],
"cnName": json['name'],
"routerName": routerName,
"catId": int.parse(json['parentId'])
};
list.add(WidgetPoint.fromJSON(tempMap));
}
return list;
} else {
return [];
}
}
}

View File

@ -4,12 +4,13 @@
/// @Last Modified time: 2019-06-05 14:01:03
import 'package:flutter/material.dart';
import 'package:event_bus/event_bus.dart';
import 'package:fluro/fluro.dart';
import 'package:flutter_go/model/collection.dart';
import 'package:flutter_go/routers/application.dart';
import 'package:flutter_go/routers/routers.dart';
import 'package:flutter_go/event/event_bus.dart';
import 'package:flutter_go/event/event_model.dart';
import 'package:flutter_go/utils/data_utils.dart';
class CollectionFullPage extends StatefulWidget {
final bool hasLogined;
@ -41,13 +42,10 @@ class _CollectionFullPageState extends State<CollectionFullPage> {
void _getList() {
_collectionList.clear();
_collectionControl.getAllCollection().then((resultList) {
resultList.forEach((item) {
_collectionList.add(item);
});
DataUtils.getAllCollections(context).then((collectionList) {
if (this.mounted) {
setState(() {
_collectionList = _collectionList;
_collectionList = collectionList;
});
}
});
@ -73,7 +71,7 @@ class _CollectionFullPageState extends State<CollectionFullPage> {
SizedBox(
width: 5.0,
),
Text('模拟器重新运行会丢失收藏'),
Text('常用的组件都可以收藏在这里哦'),
],
),
);
@ -115,21 +113,24 @@ class _CollectionFullPageState extends State<CollectionFullPage> {
trailing:
Icon(Icons.keyboard_arrow_right, color: Colors.grey, size: 30.0),
onTap: () {
if (_collectionList[index - 1].router.contains('http')) {
// 注意这里title已经转义过了
Application.router.navigateTo(context,
'${Routes.webViewPage}?title=${_collectionList[index - 1].name}&url=${Uri.encodeComponent(_collectionList[index - 1].router)}');
} else {
Application.router
.navigateTo(context, "${_collectionList[index - 1].router}");
}
Application.router.navigateTo(
context, _collectionList[index - 1].router,
transition: TransitionType.inFromRight);
// if (_collectionList[index - 1].router.contains('http')) {
// // 注意这里title已经转义过了
// Application.router.navigateTo(context,
// '${Routes.webViewPage}?title=${_collectionList[index - 1].name}&url=${Uri.encodeComponent(_collectionList[index - 1].router)}');
// } else {
// Application.router
// .navigateTo(context, "${_collectionList[index - 1].router}");
// }
},
),
);
}
ListView buildContent(){
ListView buildContent() {
if (_collectionList.length == 0) {
return ListView(
children: <Widget>[

View File

@ -11,6 +11,7 @@ import 'package:flutter_go/routers/application.dart';
import 'package:flutter_go/routers/routers.dart';
import 'package:flutter_go/event/event_bus.dart';
import 'package:flutter_go/event/event_model.dart';
import 'package:flutter_go/utils/data_utils.dart';
class CollectionPage extends StatefulWidget {
final bool hasLogined;
@ -47,13 +48,10 @@ class _CollectionPageState extends State<CollectionPage> {
void _getList() {
_collectionList.clear();
_collectionControl.getAllCollection().then((resultList) {
resultList.forEach((item) {
_collectionList.add(item);
});
DataUtils.getAllCollections(context).then((collectionList) {
if (this.mounted) {
setState(() {
_collectionList = _collectionList;
_collectionList = collectionList;
});
}
});
@ -108,7 +106,8 @@ class _CollectionPageState extends State<CollectionPage> {
color: Theme.of(context).primaryColor,
),
title: Text(
Uri.decodeComponent(_collectionList[index - 1].name),
_collectionList[index - 1].name,
// Uri.decodeComponent(_collectionList[index - 1].name),
overflow: TextOverflow.ellipsis,
style: TextStyle(fontSize: 17.0),
),

View File

@ -7,9 +7,11 @@
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter_go/utils/data_utils.dart';
import 'package:flutter_go/utils/shared_preferences.dart';
import 'package:flutter_go/views/first_page/first_page.dart';
import 'package:flutter_go/views/first_page/main_page.dart';
import 'package:fluro/fluro.dart';
import 'package:flutter_go/views/widget_page/widget_page.dart';
import 'package:flutter_go/views/welcome_page/fourth_page.dart';
import 'package:flutter_go/views/collection_page/collection_page.dart';
@ -69,8 +71,8 @@ class _MyHomePageState extends State<AppPage>
_list
// ..add(FirstPage())
..add(MainPage(userInfo: widget.userInfo))
..add(WidgetPage(Provider.db))
..add(CollectionPage(hasLogined: widget.userInfo.id != 0))
..add(WidgetPage())
..add(CollectionPage())
..add(FourthPage());
}
@ -87,26 +89,21 @@ class _MyHomePageState extends State<AppPage>
}
void onWidgetTap(WidgetPoint widgetPoint, BuildContext context) {
List widgetDemosList = new WidgetDemoList().getDemos();
String targetName = widgetPoint.name;
String targetRouter = '/category/error/404';
widgetDemosList.forEach((item) {
if (item.name == targetName) {
targetRouter = item.routerName;
}
});
searchHistoryList
.add(SearchHistory(name: targetName, targetRouter: targetRouter));
.add(SearchHistory(name: targetName, targetRouter: widgetPoint.routerName));
print("searchHistoryList1 ${searchHistoryList.toString()}");
print("searchHistoryList2 ${targetRouter}");
print("searchHistoryList3 ${widgetPoint.name}");
Application.router.navigateTo(context, "$targetRouter");
Application.router.navigateTo(
context, widgetPoint.routerName,
transition: TransitionType.inFromRight);
}
Widget buildSearchInput(BuildContext context) {
return new SearchInput((value) async {
if (value != '') {
List<WidgetPoint> list = await widgetControl.search(value);
print('value ::: $value');
// List<WidgetPoint> list = await widgetControl.search(value);
List<WidgetPoint> list = await DataUtils.searchWidget(value);
return list
.map((item) => new MaterialSearchResult<String>(
value: item.name,

View File

@ -224,15 +224,13 @@ class _LoginPageState extends State<LoginPage> {
// 登陆操作
doLogin() {
print("doLogin");
_signInFormKey.currentState.save();
setState(() {
isLoading = true;
});
DataUtils.doLogin({'username': username, 'password': password})
.then((userResult) {
if (userResult.runtimeType == String) {
throw userResult;
}
setState(() {
isLoading = false;
});

View File

@ -0,0 +1,12 @@
const title = 'titie1';
Map<String, String> titleObjs = {
'title': 'haha33'
};
class Maps {
Map<String, String> list;
getList() {
return {
"title": "1111"
};
}
}

View File

@ -0,0 +1,128 @@
//
// Created with Android Studio.
// User: 三帆
// Date: 25/05/2019
// Time: 21:46
// email: sanfan.hx@alibaba-inc.com
// tartget: xxx
//
import 'package:flutter/material.dart';
import '../../components/widget_demo.dart';
import 'dart:convert';
import '../../components/markdown.dart' as mdCopy;
import '../../components/flutter_markdown/lib/flutter_markdown.dart';
import '../../standard_pages/index.dart';
import '../../page_demo_package/index.dart';
import 'package:flutter_go/routers/application.dart';
// ONLINE || LOCAL
ENV env = Application.env;
class StandardView extends StatefulWidget {
final String id;
final String detailMd;
StandardView({this.id, this.detailMd});
@override
_StandardView createState() => _StandardView();
}
class _StandardView extends State<StandardView> {
String markdownDesc = '# this is header';
String pageTitle = "XXX";
String author = '';
String email = '';
StandardPages standardPage = new StandardPages();
@override
void initState() {
super.initState();
this.getDetail();
}
// 本地调用的获取基本信息
Future<void> getPagesInfo() async {
String jsonString = await DefaultAssetBundle.of(context)
.loadString('lib/standard_pages/.pages.json');
List jsonList = json.decode(jsonString);
Map<String, dynamic> pageDetail =
jsonList.firstWhere((item) => item['id'] == widget.id);
if (pageDetail != null) {
setState(() {
pageTitle = pageDetail['title'] ?? '请加入title';
author = pageDetail['author'];
email = pageDetail['email'];
});
}
}
String _getContentFromLocal() {
String pageId = widget.id;
Map<String, String> pagesList = standardPage.getPages();
return pagesList[pageId];
}
Future<String> _getContentOnline() async {
String content = 'online content';
return Future(() => content);
}
Future<String> getDetail() async {
String conent = '';
print("env:::: $env");
if (env == ENV.PRODUCTION) {
conent = await _getContentOnline();
} else {
getPagesInfo();
conent = _getContentFromLocal();
}
if (this.mounted) {
setState(() {
markdownDesc = conent;
});
}
return Future(() => conent);
// this.rebuild();
}
Widget buildMarkdown() {
Map<String, String> contentList = new StandardPages().getPages();
if (contentList[widget.id] == null) {
contentList[widget.id] = '';
}
return MarkdownBody(
data: contentList[widget.id],
syntaxHighlighter: new mdCopy.HighLight(),
demoBuilder: (Map<String, dynamic> attrs) {
List<Widget> demo = demoObjects[attrs['id']];
if (demo == null) {
String errString = "not found ${attrs['id']} in demo packages";
debugPrint(errString);
demo = [Text(errString)];
}
return Column(children: demo);
});
}
@override
Widget build(BuildContext context) {
return new WidgetDemo(
title: pageTitle,
codeUrl: 'elements/Form/Button/DropdownButton/demo.dart',
contentList: [
buildMarkdown(),
SizedBox(height: 30),
'创建者: $author',
'创建者: $email',
'id: ${widget.id}',
],
docUrl:
'https://docs.flutter.io/flutter/material/DropdownButton-class.html',
);
}
}

View File

@ -23,15 +23,12 @@ class WebViewPage extends StatefulWidget {
class _WebViewPageState extends State<WebViewPage> {
final flutterWebviewPlugin = new FlutterWebviewPlugin();
bool _hasCollected = false;
String _router = '';
var _collectionIcons;
CollectionControlModel _collectionControl = new CollectionControlModel();
final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
@override
void initState() {
super.initState();
flutterWebviewPlugin.onUrlChanged.listen((String url) {
print('url change:$url');
if (url.indexOf('loginSuccess') > -1) {
@ -64,87 +61,14 @@ class _WebViewPageState extends State<WebViewPage> {
flutterWebviewPlugin.close();
}
});
_collectionControl
.getRouterByName(Uri.encodeComponent(widget.title.trim()))
.then((list) {
list.forEach((item) {
if (widget.title.trim() == item['name']) {
_router = item['router'];
}
});
if (mounted) {
setState(() {
_hasCollected = list.length > 0;
});
}
});
}
// 点击收藏按钮
_getCollection() {
if (_hasCollected) {
// 删除操作
_collectionControl
.deleteByName(Uri.encodeComponent(widget.title.trim()))
.then((result) {
if (result > 0 && this.mounted) {
setState(() {
_hasCollected = false;
});
_scaffoldKey.currentState
.showSnackBar(SnackBar(content: Text('已取消收藏')));
if (ApplicationEvent.event != null) {
ApplicationEvent.event
.fire(CollectionEvent(widget.title, _router, true));
}
return;
}
print('删除错误');
});
} else {
// 插入操作
_collectionControl
.insert(Collection(
name: Uri.encodeComponent(widget.title.trim()),
router: widget.url))
.then((result) {
if (this.mounted) {
setState(() {
_hasCollected = true;
});
if (ApplicationEvent.event != null) {
ApplicationEvent.event
.fire(CollectionEvent(widget.title, _router, false));
}
_scaffoldKey.currentState
.showSnackBar(SnackBar(content: Text('收藏成功')));
}
});
}
}
@override
Widget build(BuildContext context) {
if (_hasCollected) {
_collectionIcons = Icons.favorite;
} else {
_collectionIcons = Icons.favorite_border;
}
return Scaffold(
key: _scaffoldKey,
appBar: AppBar(
title: Text(widget.title),
actions: <Widget>[
new IconButton(
tooltip: 'goBack home',
onPressed: _getCollection,
icon: Icon(
_collectionIcons,
),
),
],
),
body: WebviewScaffold(
url: widget.url,

View File

@ -6,28 +6,28 @@
import 'package:flutter/material.dart';
import 'package:flutter_go/components/cate_card.dart';
import 'package:flutter_go/model/cat.dart';
import 'package:flutter_go/routers/application.dart';
class WidgetPage extends StatefulWidget {
final db;
final CatControlModel catModel;
WidgetPage(this.db)
: catModel = new CatControlModel(),
super();
@override
SecondPageState createState() => new SecondPageState(catModel);
SecondPageState createState() => new SecondPageState();
}
class SecondPageState extends State<WidgetPage> with AutomaticKeepAliveClientMixin{
CatControlModel catModel;
SecondPageState(this.catModel) : super();
SecondPageState() : super();
TextEditingController controller;
String active = 'test';
String data = '';
List<Cat> categories = [];
@override
bool get wantKeepAlive => true;
@ -35,25 +35,16 @@ class SecondPageState extends State<WidgetPage> with AutomaticKeepAliveClientMix
@override
void initState() {
super.initState();
renderCats();
}
void renderCats() {
catModel.getList().then((List data) {
if (data.isNotEmpty) {
setState(() {
categories = data;
});
}
});
}
Widget buildGrid() {
// 存放最后的widget
List<Widget> tiles = [];
for (Cat item in categories) {
Application.widgetTree.children.forEach((dynamic item) {
tiles.add(new CateCard(category: item));
}
});
return new ListView(
children: tiles,
);
@ -62,11 +53,7 @@ class SecondPageState extends State<WidgetPage> with AutomaticKeepAliveClientMix
@override
Widget build(BuildContext context) {
super.build(context);
if (categories.length == 0) {
return ListView(
children: <Widget>[new Container()],
);
}
print("build in widget page");
return Container(
color: Theme.of(context).backgroundColor,
child: this.buildGrid(),

View File

@ -15,7 +15,7 @@ class _CheckedPopupMenuItemDemoState extends State<CheckedPopupMenuItemDemo> {
final String _checkedValue1 = 'One';
final String _checkedValue2 = 'Two';
final String _checkedValue3 = 'Free';
final String _checkedValue3 = 'Three';
final String _checkedValue4 = 'Four';
@override

View File

@ -11,7 +11,7 @@ class DropdownMenuItemDemo extends StatefulWidget {
class _DropdownMenuItemDemoState extends State<DropdownMenuItemDemo> {
String dropdown1Value = 'Free';
String dropdown1Value = 'Three';
String dropdown2Value;
String dropdown3Value = 'Four';
@ -32,7 +32,7 @@ class _DropdownMenuItemDemoState extends State<DropdownMenuItemDemo> {
dropdown1Value = newValue;
});
},
items: <String>['One', 'Two', 'Free', 'Four'].map<DropdownMenuItem<String>>((String value) {
items: <String>['One', 'Two', 'Three', 'Four'].map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
@ -53,7 +53,7 @@ class _DropdownMenuItemDemoState extends State<DropdownMenuItemDemo> {
dropdown2Value = newValue;
});
},
items: <String>['One', 'Two', 'Free', 'Four'].map<DropdownMenuItem<String>>((String value) {
items: <String>['One', 'Two', 'Three', 'Four'].map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
@ -74,7 +74,7 @@ class _DropdownMenuItemDemoState extends State<DropdownMenuItemDemo> {
});
},
items: <String>[
'One', 'Two', 'Free', 'Four', 'Can', 'I', 'Have', 'A', 'Little',
'One', 'Two', 'Three', 'Four', 'Can', 'I', 'Have', 'A', 'Little',
'Bit', 'More', 'Five', 'Six', 'Seven', 'Eight', 'Nine', 'Ten'
]
.map<DropdownMenuItem<String>>((String value) {

View File

@ -21,7 +21,7 @@ const String content1 = '''
''';
class Demo extends StatefulWidget {
static const String routeName = '/themes/Material/MaterialColor';
static const String routeName = '/Themes/Material/MaterialColor';
_DemoState createState() => _DemoState();
}

View File

@ -105,7 +105,7 @@ packages:
name: fluro
url: "https://pub.dartlang.org"
source: hosted
version: "1.5.0"
version: "1.5.1"
flutter:
dependency: "direct main"
description: flutter
@ -124,7 +124,7 @@ packages:
name: flutter_downloader
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.7"
version: "1.1.9"
flutter_jpush:
dependency: "direct main"
description:
@ -132,13 +132,6 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "0.0.4"
flutter_markdown:
dependency: "direct main"
description:
name: flutter_markdown
url: "https://pub.dartlang.org"
source: hosted
version: "0.2.0"
flutter_spinkit:
dependency: "direct main"
description:
@ -194,7 +187,7 @@ packages:
source: hosted
version: "1.0.7"
markdown:
dependency: transitive
dependency: "direct main"
description:
name: markdown
url: "https://pub.dartlang.org"
@ -208,7 +201,7 @@ packages:
source: hosted
version: "0.12.5"
meta:
dependency: transitive
dependency: "direct main"
description:
name: meta
url: "https://pub.dartlang.org"
@ -234,9 +227,9 @@ packages:
name: package_info
url: "https://pub.dartlang.org"
source: hosted
version: "0.4.0+4"
version: "0.4.0+6"
path:
dependency: transitive
dependency: "direct main"
description:
name: path
url: "https://pub.dartlang.org"
@ -248,7 +241,7 @@ packages:
name: path_provider
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.2"
version: "1.2.0"
pedantic:
dependency: transitive
description:
@ -297,7 +290,7 @@ packages:
name: share
url: "https://pub.dartlang.org"
source: hosted
version: "0.6.2"
version: "0.6.2+1"
shared_preferences:
dependency: "direct main"
description:
@ -323,7 +316,7 @@ packages:
name: sqflite
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.6+1"
version: "1.1.6+3"
stack_trace:
dependency: transitive
description:
@ -339,7 +332,7 @@ packages:
source: hosted
version: "2.0.0"
string_scanner:
dependency: transitive
dependency: "direct main"
description:
name: string_scanner
url: "https://pub.dartlang.org"
@ -379,7 +372,7 @@ packages:
name: url_launcher
url: "https://pub.dartlang.org"
source: hosted
version: "5.0.5"
version: "5.1.1"
vector_math:
dependency: transitive
description:
@ -396,4 +389,4 @@ packages:
version: "0.0.1"
sdks:
dart: ">=2.2.2 <3.0.0"
flutter: ">=1.5.0 <2.0.0"
flutter: ">=1.6.0 <2.0.0"

View File

@ -10,7 +10,7 @@ description: flutter_go
version: 1.0.6
environment:
sdk: ">=2.0.0-dev.68.0 <3.0.0"
sdk: ">=2.1.0 <3.0.0"
dependencies:
flutter:
@ -23,7 +23,6 @@ dependencies:
fluro: ^1.3.4
image_picker: ^0.5.0
sqflite: ^1.1.5
flutter_markdown: ^0.2.0
url_launcher: ^5.0.2
# 本地存储、收藏功能
shared_preferences: ^0.4.3
@ -42,6 +41,10 @@ dependencies:
flutter_bloc: ^0.11.1
bloc: ^0.12.0
html: ^0.14.0+2
markdown: ^2.0.0
meta: ^1.0.5
string_scanner: ^1.0.0
path: ^1.5.1
flutter_downloader: ^1.1.7
path_provider: ^1.1.0
permission_handler: ^3.0.0
@ -58,7 +61,6 @@ dev_dependencies:
# For information on the generic Dart part of this file, see the
# following page: https://www.dartlang.org/tools/pub/pubspec
# The following section is specific to Flutter.
flutter:
@ -209,6 +211,8 @@ flutter:
- lib/widgets/themes/Cupertino/CupertinoTabScaffold/demo.dart
- lib/widgets/themes/Cupertino/CupertinoTabView/demo.dart
- lib/widgets/themes/Cupertino/CupertinoTimerPicker/demo.dart
- lib/page_demo_package/.demo.json
- lib/standard_pages/.pages.json
- assets/app.db
- assets/images/
- assets/fonts/