Merge branch 'cli' of github.com:alibaba/flutter-go

This commit is contained in:
sanfan.hx
2019-07-25 12:16:14 +08:00
66 changed files with 2877 additions and 268 deletions

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';//获取全部收藏
@ -28,4 +28,6 @@ class Api{
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列表树
}

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

@ -64,25 +64,29 @@ 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;
_collectionControl.getRouterByUrl(currentPath).then((list) {
if (this.mounted) {
setState(() {
_hasCollected = list.length > 0;
});
}
});
if (this.mounted) {
setState(() {
_hasCollected = list.length > 0;
});
}
});
}
// 点击收藏按钮
_getCollection() {
String currentRouterPath = ModalRoute.of(context).settings.name;
if (_hasCollected) {
// 删除操作
_collectionControl.deleteByName(widget.title).then((result) {
_collectionControl.deleteByPath(currentRouterPath).then((result) {
if (result > 0 && this.mounted) {
setState(() {
_hasCollected = false;
@ -91,7 +95,7 @@ class _WidgetDemoState extends State<WidgetDemo> {
.showSnackBar(SnackBar(content: Text('已取消收藏')));
if (ApplicationEvent.event != null) {
ApplicationEvent.event
.fire(CollectionEvent(widget.title, _router, true));
.fire(CollectionEvent(widget.title, currentRouterPath, true));
}
return;
@ -101,7 +105,7 @@ class _WidgetDemoState extends State<WidgetDemo> {
} else {
// 插入操作
_collectionControl
.insert(Collection(name: widget.title, router: _router))
.insert(Collection(name: widget.title, router: currentRouterPath))
.then((result) {
if (this.mounted) {
setState(() {
@ -110,7 +114,7 @@ class _WidgetDemoState extends State<WidgetDemo> {
if (ApplicationEvent.event != null) {
ApplicationEvent.event
.fire(CollectionEvent(widget.title, _router, false));
.fire(CollectionEvent(widget.title, currentRouterPath, false));
}
_scaffoldKey.currentState

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) {
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();
@ -26,6 +26,8 @@ class Routes {
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 +43,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

@ -104,4 +104,15 @@ class DataUtils {
return false;
}
}
/// 获取widget列表处的树型数据
static Future<List> getWidgetTreeList() async {
var response = await NetUtils.get(Api.GET_WIDGET_TREE);
if (response['success']) {
return response['data'];
} else {
return [];
}
}
}

View File

@ -51,6 +51,7 @@ class _CollectionPageState extends State<CollectionPage> {
resultList.forEach((item) {
_collectionList.add(item);
});
print("_collectionList ${_collectionList}");
if (this.mounted) {
setState(() {
_collectionList = _collectionList;
@ -108,7 +109,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

@ -69,8 +69,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());
}

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,129 @@
//
// 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();
}
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

@ -32,6 +32,7 @@ class _WebViewPageState extends State<WebViewPage> {
@override
void initState() {
super.initState();
flutterWebviewPlugin.onUrlChanged.listen((String url) {
print('url change:$url');
if (url.indexOf('loginSuccess') > -1) {
@ -64,21 +65,21 @@ 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;
});
}
});
// 这里 存放不使用 name 改成 url. 确定唯一性
// _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;
// });
// }
// });
}
// 点击收藏按钮

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,17 @@ 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) {
print("Application.widgetTree>>> ${Application.widgetTree}");
Application.widgetTree.children.forEach((dynamic item) {
tiles.add(new CateCard(category: item));
}
});
return new ListView(
children: tiles,
);
@ -62,11 +54,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

@ -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();
}