mirror of
https://github.com/alibaba/flutter-go.git
synced 2025-07-10 04:26:28 +08:00
Merge branch 'develop' of github.com:alibaba/flutter-common-widgets-app into develop
This commit is contained in:
BIN
assets/images/calendar.png
Normal file
BIN
assets/images/calendar.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 555 B |
BIN
assets/images/house.png
Normal file
BIN
assets/images/house.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 978 B |
BIN
assets/images/p1.png
Normal file
BIN
assets/images/p1.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.2 MiB |
BIN
assets/images/p2.png
Normal file
BIN
assets/images/p2.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 853 KiB |
BIN
assets/images/p3.png
Normal file
BIN
assets/images/p3.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 871 KiB |
BIN
assets/images/plane.png
Normal file
BIN
assets/images/plane.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.1 KiB |
@ -14,8 +14,6 @@ import 'package:flutter/services.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
const String _kStartTag = '// START ';
|
||||
const String _kEndTag = '// END';
|
||||
|
||||
Map<String, String> _exampleCode;
|
||||
String _code;
|
||||
@ -38,7 +36,6 @@ Future<void> _parseExampleCode(context,String filePath, AssetBundle bundle) asyn
|
||||
try {
|
||||
code = await bundle.loadString('lib/widgets/$filePath');
|
||||
} catch (err) {
|
||||
print('${Application.github['widgetsURL']} $filePath');
|
||||
Navigator.of(context).pop();
|
||||
_launchURL(Application.github['widgetsURL'] + filePath);
|
||||
}
|
||||
|
@ -1,5 +1,14 @@
|
||||
/*
|
||||
* @Author: 一凨
|
||||
* @Date: 2019-01-14 17:53:54
|
||||
* @Last Modified by: 一凨
|
||||
* @Last Modified time: 2019-01-14 17:57:51
|
||||
*/
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
import '../routers/application.dart';
|
||||
import '../routers/routers.dart';
|
||||
import 'dart:core';
|
||||
|
||||
|
||||
class ListViewItem extends StatelessWidget {
|
||||
final String itemUrl;
|
||||
@ -9,15 +18,6 @@ class ListViewItem extends StatelessWidget {
|
||||
const ListViewItem({Key key, this.itemUrl, this.itemTitle, this.data})
|
||||
: super(key: key);
|
||||
|
||||
void _launchURL(String url, BuildContext context) async {
|
||||
|
||||
if (await canLaunch(url)) {
|
||||
await launch(url);
|
||||
} else {
|
||||
throw 'Could not launch $url';
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@ -27,7 +27,8 @@ class ListViewItem extends StatelessWidget {
|
||||
margin: new EdgeInsets.symmetric(horizontal: 10.0, vertical: 6.0),
|
||||
child: ListTile(
|
||||
onTap: () {
|
||||
_launchURL(itemUrl, context);
|
||||
// _launchURL(itemUrl, context);
|
||||
Application.router.navigateTo(context, '${Routes.webViewPage}?title=${Uri.encodeComponent(itemTitle)}&url=${Uri.encodeComponent(itemUrl)}');
|
||||
},
|
||||
title: Padding(
|
||||
child: Text(
|
||||
|
@ -46,7 +46,6 @@ class Provider {
|
||||
|
||||
for(int i = 0; i < expectTables.length; i++) {
|
||||
if (!tables.contains(expectTables[i])) {
|
||||
print("table lost in app");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -4,17 +4,14 @@
|
||||
* 新widget详情页模板
|
||||
*/
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
import '../routers/application.dart';
|
||||
import '../routers/routers.dart';
|
||||
import '../components/markdown.dart';
|
||||
import '../model/collection.dart';
|
||||
import '../widgets/index.dart';
|
||||
import 'package:fluttertoast/fluttertoast.dart';
|
||||
import '../event/event-bus.dart';
|
||||
import '../event/event-model.dart';
|
||||
import './full_screen_code_dialog.dart';
|
||||
import '../routers/application.dart';
|
||||
import '../routers/routers.dart';
|
||||
import 'dart:core';
|
||||
|
||||
class WidgetDemo extends StatefulWidget {
|
||||
@ -40,10 +37,9 @@ class WidgetDemo extends StatefulWidget {
|
||||
class _WidgetDemoState extends State<WidgetDemo> {
|
||||
bool _hasCollected = false;
|
||||
CollectionControlModel _collectionControl = new CollectionControlModel();
|
||||
Color _collectionColor;
|
||||
var _collectionIcons;
|
||||
List widgetDemosList = new WidgetDemoList().getDemos();
|
||||
String _router = '';
|
||||
String _collText = '';
|
||||
|
||||
void showInSnackBar(String value) {
|
||||
Fluttertoast.showToast(
|
||||
@ -55,13 +51,6 @@ class _WidgetDemoState extends State<WidgetDemo> {
|
||||
textColor: Colors.white);
|
||||
}
|
||||
|
||||
void _launchURL(String url) async {
|
||||
if (await canLaunch(url)) {
|
||||
await launch(url);
|
||||
} else {
|
||||
throw 'Could not launch $url';
|
||||
}
|
||||
}
|
||||
|
||||
List<Widget> _buildContent() {
|
||||
List<Widget> _list = [
|
||||
@ -143,24 +132,20 @@ class _WidgetDemoState extends State<WidgetDemo> {
|
||||
|
||||
void _selectValue(value){
|
||||
if(value == 'doc'){
|
||||
_launchURL(widget.docUrl);
|
||||
// _launchURL(widget.docUrl);
|
||||
Application.router.navigateTo(context, '${Routes.webViewPage}?title=${Uri.encodeComponent(widget.title)} Doc&&url=${Uri.encodeComponent(widget.docUrl)}');
|
||||
}else if(value =='code'){
|
||||
// _launchURL(Application.github['widgetsURL'] + widget.codeUrl);
|
||||
Application.router.navigateTo(context, '${Routes.codeView}?filePath=${Uri.encodeComponent(widget.codeUrl)}');
|
||||
|
||||
}else{
|
||||
_getCollection();
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (_hasCollected) {
|
||||
_collectionColor = Colors.red;
|
||||
_collText='取消收藏';
|
||||
_collectionIcons = Icons.favorite;
|
||||
} else {
|
||||
_collectionColor =null;
|
||||
_collText='组件收藏';
|
||||
_collectionIcons = Icons.favorite_border;
|
||||
}
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
@ -173,6 +158,11 @@ class _WidgetDemoState extends State<WidgetDemo> {
|
||||
},
|
||||
icon: Icon(Icons.home),
|
||||
),
|
||||
new IconButton(
|
||||
tooltip: 'collection',
|
||||
onPressed: _getCollection,
|
||||
icon: Icon(_collectionIcons),
|
||||
),
|
||||
PopupMenuButton<String>(
|
||||
onSelected: _selectValue,
|
||||
itemBuilder: (BuildContext context) => <PopupMenuEntry<String>>[
|
||||
@ -191,15 +181,6 @@ class _WidgetDemoState extends State<WidgetDemo> {
|
||||
title: Text('查看Demo'),
|
||||
),
|
||||
),
|
||||
const PopupMenuDivider(),
|
||||
PopupMenuItem<String>(
|
||||
value: 'collection',
|
||||
child: ListTile(
|
||||
leading: Icon(Icons.star,size: 22.0,color: _collectionColor,),
|
||||
title: Text(_collText),
|
||||
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
|
192
lib/components/fourth_page_feature/page_dragger.dart
Normal file
192
lib/components/fourth_page_feature/page_dragger.dart
Normal file
@ -0,0 +1,192 @@
|
||||
|
||||
import 'dart:async';
|
||||
import 'dart:ui';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import './pager_indicator.dart';
|
||||
|
||||
class PageDragger extends StatefulWidget {
|
||||
|
||||
final canDragLeftToRight;
|
||||
final canDragRightToLeft;
|
||||
|
||||
final StreamController<SlideUpdate> slideUpdateStream;
|
||||
|
||||
|
||||
PageDragger({
|
||||
this.canDragLeftToRight,
|
||||
this.canDragRightToLeft,
|
||||
this.slideUpdateStream,
|
||||
});
|
||||
|
||||
@override
|
||||
_PageDraggerState createState() => _PageDraggerState();
|
||||
}
|
||||
|
||||
class _PageDraggerState extends State<PageDragger> {
|
||||
|
||||
static const FULL_TRANSTITION_PX = 300.0;
|
||||
|
||||
Offset dragStart;
|
||||
SlideDirection slideDirection;
|
||||
double slidePercent = 0.0;
|
||||
|
||||
onDragStart(DragStartDetails details){
|
||||
dragStart = details.globalPosition;
|
||||
}
|
||||
|
||||
onDragUpdate(DragUpdateDetails details) {
|
||||
if (dragStart != null) {
|
||||
final newPosition = details.globalPosition;
|
||||
final dx = dragStart.dx - newPosition.dx;
|
||||
|
||||
if (dx > 0 && widget.canDragRightToLeft) {
|
||||
slideDirection = SlideDirection.rightToLeft;
|
||||
} else if (dx < 0 && widget.canDragLeftToRight) {
|
||||
slideDirection = SlideDirection.leftToRight;
|
||||
} else {
|
||||
slideDirection = SlideDirection.none;
|
||||
}
|
||||
|
||||
if (slideDirection != SlideDirection.none){
|
||||
slidePercent = (dx / FULL_TRANSTITION_PX).abs().clamp(0.0, 1.0);
|
||||
} else {
|
||||
slidePercent = 0.0;
|
||||
}
|
||||
widget.slideUpdateStream.add(
|
||||
new SlideUpdate(
|
||||
UpdateType.dragging,
|
||||
slideDirection,
|
||||
slidePercent
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
onDragEnd(DragEndDetails details){
|
||||
widget.slideUpdateStream.add(
|
||||
new SlideUpdate(
|
||||
UpdateType.doneDragging,
|
||||
SlideDirection.none,
|
||||
0.0,
|
||||
)
|
||||
);
|
||||
|
||||
dragStart = null;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return GestureDetector(
|
||||
onHorizontalDragStart: onDragStart ,
|
||||
onHorizontalDragUpdate: onDragUpdate ,
|
||||
onHorizontalDragEnd: onDragEnd ,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class AnimatedPageDragger{
|
||||
|
||||
static const PERCENT_PER_MILLISECOND = 0.005;
|
||||
|
||||
final slideDirection;
|
||||
final transitionGoal;
|
||||
|
||||
AnimationController completionAnimationController;
|
||||
|
||||
AnimatedPageDragger({
|
||||
this.slideDirection,
|
||||
this.transitionGoal,
|
||||
slidePercent,
|
||||
StreamController<SlideUpdate> slideUpdateStream,
|
||||
TickerProvider vsync,
|
||||
}) {
|
||||
final startSlidePercent = slidePercent;
|
||||
var endSlidePercent;
|
||||
var duration;
|
||||
|
||||
if ( transitionGoal == TransitionGoal.open){
|
||||
endSlidePercent = 1.0;
|
||||
|
||||
final slideRemaining = 1.0 - slidePercent;
|
||||
|
||||
duration = new Duration(
|
||||
milliseconds: (slideRemaining / PERCENT_PER_MILLISECOND).round()
|
||||
);
|
||||
|
||||
} else {
|
||||
endSlidePercent = 0.0;
|
||||
duration = new Duration(
|
||||
milliseconds: (slidePercent / PERCENT_PER_MILLISECOND).round()
|
||||
);
|
||||
}
|
||||
|
||||
completionAnimationController = new AnimationController(
|
||||
duration: duration,
|
||||
vsync: vsync
|
||||
)
|
||||
..addListener((){
|
||||
slidePercent = lerpDouble(
|
||||
startSlidePercent,
|
||||
endSlidePercent,
|
||||
completionAnimationController.value
|
||||
);
|
||||
|
||||
slideUpdateStream.add(
|
||||
new SlideUpdate(
|
||||
UpdateType.animating,
|
||||
slideDirection,
|
||||
slidePercent,
|
||||
)
|
||||
);
|
||||
|
||||
})
|
||||
|
||||
..addStatusListener((AnimationStatus status){
|
||||
|
||||
if(status == AnimationStatus.completed){
|
||||
slideUpdateStream.add(
|
||||
new SlideUpdate(
|
||||
UpdateType.doneAnimating,
|
||||
slideDirection,
|
||||
endSlidePercent,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
run(){
|
||||
completionAnimationController.forward(from: 0.0);
|
||||
}
|
||||
|
||||
dispose(){
|
||||
completionAnimationController.dispose();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
enum TransitionGoal{
|
||||
open,
|
||||
close,
|
||||
}
|
||||
|
||||
enum UpdateType{
|
||||
dragging,
|
||||
doneDragging,
|
||||
animating,
|
||||
doneAnimating,
|
||||
}
|
||||
|
||||
class SlideUpdate {
|
||||
final updateType;
|
||||
final direction;
|
||||
final slidePercent;
|
||||
|
||||
SlideUpdate(
|
||||
this.updateType,
|
||||
this.direction,
|
||||
this.slidePercent
|
||||
);
|
||||
}
|
53
lib/components/fourth_page_feature/page_reveal.dart
Normal file
53
lib/components/fourth_page_feature/page_reveal.dart
Normal file
@ -0,0 +1,53 @@
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class PageReveal extends StatelessWidget {
|
||||
|
||||
final double revealPercent;
|
||||
final Widget child;
|
||||
|
||||
PageReveal({
|
||||
this.revealPercent,
|
||||
this.child
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ClipOval(
|
||||
clipper: new CircleRevealClipper(revealPercent),
|
||||
child: child,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class CircleRevealClipper extends CustomClipper<Rect>{
|
||||
|
||||
final double revealPercent;
|
||||
|
||||
|
||||
CircleRevealClipper(
|
||||
this.revealPercent
|
||||
);
|
||||
|
||||
@override
|
||||
Rect getClip(Size size) {
|
||||
|
||||
final epicenter = new Offset(size.width / 2, size.height * 0.9);
|
||||
|
||||
double theta = atan(epicenter.dy / epicenter.dx);
|
||||
final distanceToCorner = epicenter.dy / sin(theta);
|
||||
|
||||
final radius = distanceToCorner * revealPercent;
|
||||
final diameter = 2 * radius;
|
||||
|
||||
return new Rect.fromLTWH(epicenter.dx - radius, epicenter.dy - radius, diameter, diameter);
|
||||
}
|
||||
|
||||
@override
|
||||
bool shouldReclip(CustomClipper<Rect> oldClipper) {
|
||||
// TODO: implement shouldReclip
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
156
lib/components/fourth_page_feature/pager_indicator.dart
Normal file
156
lib/components/fourth_page_feature/pager_indicator.dart
Normal file
@ -0,0 +1,156 @@
|
||||
import 'dart:ui';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import './pages.dart';
|
||||
|
||||
class PagerIndicator extends StatelessWidget {
|
||||
|
||||
final PagerIndicatorViewModel viewModel;
|
||||
|
||||
PagerIndicator({
|
||||
this.viewModel,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
||||
List<PageBubble> bubbles = [];
|
||||
for(var i = 0; i < viewModel.pages.length; ++i ){
|
||||
final page = viewModel.pages[i];
|
||||
|
||||
var percentActive;
|
||||
|
||||
if(i == viewModel.activeIndex){
|
||||
percentActive = 1.0 - viewModel.slidePercent;
|
||||
} else if (i == viewModel.activeIndex - 1 && viewModel.slideDirection == SlideDirection.leftToRight){
|
||||
percentActive = viewModel.slidePercent;
|
||||
} else if (i == viewModel.activeIndex + 1 && viewModel.slideDirection == SlideDirection.rightToLeft){
|
||||
percentActive = viewModel.slidePercent;
|
||||
}else {
|
||||
percentActive = 0.0;
|
||||
}
|
||||
|
||||
bool isHollow = i > viewModel.activeIndex || (i == viewModel.activeIndex && viewModel.slideDirection == SlideDirection.leftToRight);
|
||||
|
||||
|
||||
|
||||
bubbles.add(
|
||||
new PageBubble(
|
||||
viewModel: new PageBubbleViewModel(
|
||||
page.iconAssetPath,
|
||||
page.color,
|
||||
isHollow,
|
||||
percentActive,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
final BUBBLE_WIDHT = 55.0 ;
|
||||
final baseTranslation = ((viewModel.pages.length * BUBBLE_WIDHT) / 2) - (BUBBLE_WIDHT / 2) ;
|
||||
var translation = baseTranslation - (viewModel.activeIndex * BUBBLE_WIDHT);
|
||||
|
||||
if (viewModel.slideDirection == SlideDirection.leftToRight){
|
||||
translation += BUBBLE_WIDHT * viewModel.slidePercent;
|
||||
}else if (viewModel.slideDirection == SlideDirection.rightToLeft){
|
||||
translation -= BUBBLE_WIDHT * viewModel.slidePercent;
|
||||
}
|
||||
|
||||
return new Column(
|
||||
children: <Widget>[
|
||||
new Expanded(child: new Container()),
|
||||
new Transform(
|
||||
transform: new Matrix4.translationValues(translation, 0.0, 0.0),
|
||||
child: new Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: bubbles,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
enum SlideDirection{
|
||||
leftToRight,
|
||||
rightToLeft,
|
||||
none,
|
||||
}
|
||||
|
||||
|
||||
class PagerIndicatorViewModel{
|
||||
final List<PageViewModel> pages;
|
||||
final int activeIndex;
|
||||
final SlideDirection slideDirection;
|
||||
final double slidePercent;
|
||||
|
||||
PagerIndicatorViewModel(
|
||||
this.pages,
|
||||
this.activeIndex,
|
||||
this.slideDirection,
|
||||
this.slidePercent
|
||||
);
|
||||
|
||||
|
||||
}
|
||||
|
||||
class PageBubble extends StatelessWidget {
|
||||
|
||||
final PageBubbleViewModel viewModel;
|
||||
|
||||
|
||||
PageBubble({
|
||||
this.viewModel
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return new Container(
|
||||
width: 55.0,
|
||||
height: 65.0,
|
||||
child: new Center(
|
||||
child: new Container(
|
||||
width: lerpDouble(20.0,45.0,viewModel.activePercent),
|
||||
height: lerpDouble(20.0,45.0,viewModel.activePercent),
|
||||
decoration: new BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
color: viewModel.isHollow
|
||||
? const Color(0x88FFFFFF).withAlpha(0x88 * viewModel.activePercent.round())
|
||||
: const Color(0x88FFFFFF),
|
||||
border: new Border.all(
|
||||
color: viewModel.isHollow
|
||||
? const Color(0x88FFFFFF).withAlpha((0x88 * (1.0 - viewModel.activePercent)).round())
|
||||
: Colors.transparent,
|
||||
width: 3.0,
|
||||
),
|
||||
),
|
||||
child: new Opacity(
|
||||
opacity: viewModel.activePercent,
|
||||
child: Image.asset(
|
||||
viewModel.iconAssetPath,
|
||||
color: viewModel.color,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class PageBubbleViewModel {
|
||||
final String iconAssetPath;
|
||||
final Color color;
|
||||
final bool isHollow;
|
||||
final double activePercent;
|
||||
|
||||
PageBubbleViewModel (
|
||||
this.iconAssetPath,
|
||||
this.color,
|
||||
this.isHollow,
|
||||
this.activePercent,
|
||||
);
|
||||
|
||||
|
||||
}
|
||||
|
136
lib/components/fourth_page_feature/pages.dart
Normal file
136
lib/components/fourth_page_feature/pages.dart
Normal file
@ -0,0 +1,136 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import '../../routers/application.dart';
|
||||
import '../../routers/routers.dart';
|
||||
|
||||
final pages = [
|
||||
new PageViewModel(
|
||||
const Color(0xFFcd344f),
|
||||
//'assets/mountain.png',
|
||||
'assets/images/p2.png',
|
||||
'FlutterGo是什么?',
|
||||
'【FlutterGo】 是由"阿里拍卖"前端团队几位 Flutter 粉丝,用业余时间开发的一款,用于 Flutter 教学帮助的App,这里没有高大尚的概念,只有一个一个亲历的尝试,用最直观的方式展示的 Flutter 官方demo',
|
||||
'assets/images/plane.png'),
|
||||
new PageViewModel(
|
||||
const Color(0xFF638de3),
|
||||
//'assets/world.png',
|
||||
'assets/images/p1.png',
|
||||
'FLutterGo的背景',
|
||||
'🐢 官网文档示例较不够健全,不够直观\n🐞 运行widget demo要到处翻阅资料\n🐌 英文文档翻译生涩难懂,学习资料太少\n',
|
||||
'assets/images/calendar.png'),
|
||||
new PageViewModel(
|
||||
const Color(0xFFFF682D),
|
||||
//'assets/home.png',
|
||||
'assets/images/p3.png',
|
||||
'FlutterGo的特点',
|
||||
'🐡 详解常用widget多达 130+ 个\n🦋 持续迭代追新官方版本\n🐙 配套Demo详解widget用法\n🚀 一站式搞定所有常用widget,开箱即查\n',
|
||||
'assets/images/house.png',
|
||||
),
|
||||
];
|
||||
|
||||
class Page extends StatelessWidget {
|
||||
final PageViewModel viewModel;
|
||||
final double percentVisible;
|
||||
|
||||
Page({
|
||||
this.viewModel,
|
||||
this.percentVisible = 1.0,
|
||||
});
|
||||
|
||||
Widget creatButton(BuildContext context,String txt,IconData iconName,String type){
|
||||
return RaisedButton.icon(
|
||||
onPressed: () {
|
||||
if(type == 'start'){
|
||||
//Navigator.popUntil(context, ModalRoute.withName('/'));
|
||||
}else if(type == 'goGithub'){
|
||||
Application.router.navigateTo(context, '${Routes.webViewPage}?title=${Uri.encodeComponent(txt)} Doc&&url=${Uri.encodeComponent("https://github.com/alibaba/flutter-go")}');
|
||||
}
|
||||
},
|
||||
elevation: 10.0,
|
||||
color:Colors.black26,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(20)),
|
||||
//如果不手动设置icon和text颜色,则默认使用foregroundColor颜色
|
||||
icon: new Icon(iconName,color: Colors.white,size:20.0),
|
||||
label: new Text(txt, maxLines: 1,style: TextStyle(color:Colors.white,fontSize: 16,fontWeight: FontWeight.w700),)
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return new Container(
|
||||
width: double.infinity,
|
||||
color: viewModel.color,
|
||||
child: new Opacity(
|
||||
opacity: percentVisible,
|
||||
child: new Column(
|
||||
crossAxisAlignment:CrossAxisAlignment.center,
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: [
|
||||
|
||||
new Transform(
|
||||
transform: new Matrix4.translationValues(0.0, 50.0 * (1.0 - percentVisible) ,0.0),
|
||||
child: new Padding(
|
||||
padding: new EdgeInsets.only(top: 20.0, bottom: 10.0),
|
||||
child:
|
||||
new Image.asset(
|
||||
viewModel.heroAssetPath,
|
||||
width: 200.0,
|
||||
height: 200.0),
|
||||
),
|
||||
),
|
||||
new Transform(
|
||||
transform: new Matrix4.translationValues(0.0, 30.0 * (1.0 - percentVisible) ,0.0),
|
||||
child: new Padding(
|
||||
padding: new EdgeInsets.only(top: 10.0, bottom: 10.0),
|
||||
child: new Text(
|
||||
viewModel.title,
|
||||
style: new TextStyle(
|
||||
color: Colors.white,
|
||||
fontFamily: 'FlamanteRoma',
|
||||
fontSize: 34.0,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
new Transform(
|
||||
transform: new Matrix4.translationValues(0.0, 30.0 * (1.0 - percentVisible) ,0.0),
|
||||
child: new Padding(
|
||||
padding: new EdgeInsets.only(bottom: 10.0),
|
||||
child: new Text(
|
||||
viewModel.body,
|
||||
textAlign: TextAlign.center,
|
||||
style: new TextStyle(
|
||||
height:1.2,
|
||||
color: Colors.white,
|
||||
fontFamily: 'FlamanteRomaItalic',
|
||||
fontSize: 18.0,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
ButtonBar(
|
||||
alignment: MainAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
creatButton(context,'开始使用',Icons.add_circle_outline,'start'),
|
||||
creatButton(context,'GitHub',Icons.arrow_forward,'goGithub'),
|
||||
],)
|
||||
]),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
class PageViewModel {
|
||||
final Color color;
|
||||
final String heroAssetPath;
|
||||
final String title;
|
||||
final String body;
|
||||
final String iconAssetPath;
|
||||
|
||||
PageViewModel(
|
||||
this.color,
|
||||
this.heroAssetPath,
|
||||
this.title,
|
||||
this.body,
|
||||
this.iconAssetPath,
|
||||
);
|
||||
}
|
@ -168,8 +168,8 @@ class _MyHomePageState extends State<MyHomePage>
|
||||
tabs: <Tab>[
|
||||
Tab(text: '业界动态', icon: Icon(Icons.language)),
|
||||
Tab(text: '组件', icon: Icon(Icons.extension)),
|
||||
Tab(text: '组件收藏', icon: Icon(Icons.star)),
|
||||
Tab(text: '关于手册', icon: Icon(Icons.favorite)),
|
||||
Tab(text: '组件收藏', icon: Icon(Icons.favorite)),
|
||||
Tab(text: '关于手册', icon: Icon(Icons.line_weight)),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
@ -3,24 +3,32 @@ import 'package:fluro/fluro.dart';
|
||||
import '../views/category.dart';
|
||||
import '../widgets/404.dart';
|
||||
import '../common/full_screen_code_dialog.dart';
|
||||
import '../views/web_view_page.dart';
|
||||
|
||||
var categoryHandler = new Handler(
|
||||
handlerFunc: (BuildContext context, Map<String, List<String>> params) {
|
||||
String name = params["type"]?.first;
|
||||
handlerFunc: (BuildContext context, Map<String, List<String>> params) {
|
||||
String name = params["type"]?.first;
|
||||
|
||||
return new CategoryHome(name);
|
||||
},
|
||||
);
|
||||
return new CategoryHome(name);
|
||||
},
|
||||
);
|
||||
|
||||
var widgetNotFoundHandler = new Handler(
|
||||
handlerFunc: (BuildContext context, Map<String, List<String>> params) {
|
||||
return new WidgetNotFound();
|
||||
}
|
||||
);
|
||||
handlerFunc: (BuildContext context, Map<String, List<String>> params) {
|
||||
return new WidgetNotFound();
|
||||
});
|
||||
|
||||
var fullScreenCodeDialog = new Handler(
|
||||
handlerFunc: (BuildContext context,Map<String, List<String>> params){
|
||||
String path = params['filePath']?.first;
|
||||
return new FullScreenCodeDialog(filePath: path,);
|
||||
}
|
||||
);
|
||||
handlerFunc: (BuildContext context, Map<String, List<String>> params) {
|
||||
String path = params['filePath']?.first;
|
||||
return new FullScreenCodeDialog(
|
||||
filePath: path,
|
||||
);
|
||||
});
|
||||
|
||||
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);
|
||||
});
|
||||
|
@ -8,6 +8,7 @@ class Routes {
|
||||
static String root = "/";
|
||||
static String widgetDemo = '/widget-demo';
|
||||
static String codeView = '/code-view';
|
||||
static String webViewPage = '/web-view-page';
|
||||
|
||||
static void configureRoutes(Router router) {
|
||||
List widgetDemosList = new WidgetDemoList().getDemos();
|
||||
@ -19,6 +20,7 @@ class Routes {
|
||||
router.define('/category/:type', handler: categoryHandler);
|
||||
router.define('/category/error/404', handler: widgetNotFoundHandler);
|
||||
router.define(codeView,handler:fullScreenCodeDialog);
|
||||
router.define(webViewPage,handler:webViewPageHand);
|
||||
widgetDemosList.forEach((demo) {
|
||||
Handler handler = new Handler(
|
||||
handlerFunc: (BuildContext context, Map<String, List<String>> params) {
|
||||
|
@ -2,11 +2,12 @@
|
||||
* @Author: 一凨
|
||||
* @Date: 2019-01-08 17:12:58
|
||||
* @Last Modified by: 一凨
|
||||
* @Last Modified time: 2019-01-08 20:14:56
|
||||
* @Last Modified time: 2019-01-14 20:02:46
|
||||
*/
|
||||
import 'package:flutter/material.dart';
|
||||
import '../model/collection.dart';
|
||||
import '../routers/application.dart';
|
||||
import '../routers/routers.dart';
|
||||
import '../event/event-bus.dart';
|
||||
import '../event/event-model.dart';
|
||||
import 'package:event_bus/event_bus.dart';
|
||||
@ -15,8 +16,7 @@ class CollectionPage extends StatefulWidget {
|
||||
_CollectionPageState createState() => _CollectionPageState();
|
||||
}
|
||||
|
||||
class _CollectionPageState extends State<CollectionPage>
|
||||
with AutomaticKeepAliveClientMixin {
|
||||
class _CollectionPageState extends State<CollectionPage> {
|
||||
_CollectionPageState() {
|
||||
final eventBus = new EventBus();
|
||||
ApplicationEvent.event = eventBus;
|
||||
@ -24,9 +24,7 @@ class _CollectionPageState extends State<CollectionPage>
|
||||
CollectionControlModel _collectionControl = new CollectionControlModel();
|
||||
List<Collection> _collectionList = [];
|
||||
ScrollController _scrollController = new ScrollController();
|
||||
|
||||
@override
|
||||
bool get wantKeepAlive => true;
|
||||
var _icons;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
@ -76,8 +74,17 @@ class _CollectionPageState extends State<CollectionPage>
|
||||
),
|
||||
);
|
||||
}
|
||||
if (_collectionList[index - 1].router.contains('http')) {
|
||||
if (_collectionList[index - 1].name.endsWith('Doc')) {
|
||||
_icons = Icons.library_books;
|
||||
} else {
|
||||
_icons = Icons.bookmark;
|
||||
}
|
||||
} else {
|
||||
_icons = Icons.extension;
|
||||
}
|
||||
return Container(
|
||||
padding: const EdgeInsets.all(10.0),
|
||||
padding: const EdgeInsets.symmetric(vertical: 10.0, horizontal: 5.0),
|
||||
margin: const EdgeInsets.only(bottom: 7.0),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
@ -91,15 +98,26 @@ class _CollectionPageState extends State<CollectionPage>
|
||||
],
|
||||
),
|
||||
child: ListTile(
|
||||
leading: Icon(
|
||||
_icons,
|
||||
size: 30.0,
|
||||
color: Theme.of(context).primaryColor,
|
||||
),
|
||||
title: Text(
|
||||
_collectionList[index - 1].name,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(fontSize: 17.0),
|
||||
),
|
||||
trailing:
|
||||
Icon(Icons.keyboard_arrow_right, color: Colors.grey, size: 30.0),
|
||||
onTap: () {
|
||||
Application.router
|
||||
.navigateTo(context, "${_collectionList[index - 1].router}");
|
||||
if (_collectionList[index - 1].router.contains('http')) {
|
||||
Application.router.navigateTo(context,
|
||||
'${Routes.webViewPage}?title=${Uri.encodeComponent(_collectionList[index - 1].name)}&url=${Uri.encodeComponent(_collectionList[index - 1].router)}');
|
||||
} else {
|
||||
Application.router
|
||||
.navigateTo(context, "${_collectionList[index - 1].router}");
|
||||
}
|
||||
},
|
||||
),
|
||||
);
|
||||
@ -107,7 +125,6 @@ class _CollectionPageState extends State<CollectionPage>
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
super.build(context);
|
||||
if (_collectionList.length == 0) {
|
||||
return ListView(
|
||||
children: <Widget>[
|
||||
|
@ -1,22 +1,118 @@
|
||||
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import '../components/comp_list.dart';
|
||||
import 'package:flutter_go/components/fourth_page_feature/page_dragger.dart';
|
||||
import 'package:flutter_go/components/fourth_page_feature/page_reveal.dart';
|
||||
import 'package:flutter_go/components/fourth_page_feature/pager_indicator.dart';
|
||||
import 'package:flutter_go/components/fourth_page_feature/pages.dart';
|
||||
|
||||
import '../components/comp_list.dart';
|
||||
|
||||
class FourthPage extends StatefulWidget {
|
||||
@override
|
||||
FourthPageState createState() => new FourthPageState();
|
||||
}
|
||||
|
||||
class FourthPageState extends State<FourthPage> {
|
||||
class FourthPageState extends State<FourthPage> with TickerProviderStateMixin {
|
||||
|
||||
StreamController<SlideUpdate> slideUpdateStream;
|
||||
AnimatedPageDragger animatedPageDragger;
|
||||
|
||||
int activeIndex = 0 ;
|
||||
SlideDirection slideDirection = SlideDirection.none;
|
||||
int nextPageIndex = 0 ;
|
||||
double slidePercent= 0.0;
|
||||
|
||||
FourthPageState(){
|
||||
slideUpdateStream = new StreamController<SlideUpdate>();
|
||||
|
||||
slideUpdateStream.stream.listen((SlideUpdate event){
|
||||
setState(() {
|
||||
if( event.updateType == UpdateType.dragging){
|
||||
slideDirection = event.direction;
|
||||
slidePercent = event.slidePercent;
|
||||
|
||||
if( slideDirection == SlideDirection.leftToRight ){
|
||||
nextPageIndex = activeIndex - 1;
|
||||
} else if (slideDirection == SlideDirection.rightToLeft){
|
||||
nextPageIndex = activeIndex + 1;
|
||||
} else{
|
||||
nextPageIndex = activeIndex;
|
||||
}
|
||||
} else if( event.updateType == UpdateType.doneDragging){
|
||||
if(slidePercent > 0.5){
|
||||
|
||||
animatedPageDragger = new AnimatedPageDragger(
|
||||
slideDirection: slideDirection,
|
||||
transitionGoal: TransitionGoal.open,
|
||||
slidePercent: slidePercent,
|
||||
slideUpdateStream: slideUpdateStream,
|
||||
vsync: this,
|
||||
);
|
||||
|
||||
} else{
|
||||
animatedPageDragger = new AnimatedPageDragger(
|
||||
slideDirection: slideDirection,
|
||||
transitionGoal: TransitionGoal.close,
|
||||
slidePercent: slidePercent,
|
||||
slideUpdateStream: slideUpdateStream,
|
||||
vsync: this,
|
||||
);
|
||||
|
||||
nextPageIndex = activeIndex;
|
||||
}
|
||||
|
||||
animatedPageDragger.run();
|
||||
}
|
||||
else if( event.updateType == UpdateType.animating){
|
||||
slideDirection = event.direction;
|
||||
slidePercent = event.slidePercent;
|
||||
}
|
||||
|
||||
else if (event.updateType == UpdateType.doneAnimating){
|
||||
activeIndex = nextPageIndex;
|
||||
|
||||
slideDirection = SlideDirection.none;
|
||||
slidePercent = 0.0;
|
||||
|
||||
animatedPageDragger.dispose();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return new Container(
|
||||
child: new CompList()
|
||||
return new Stack(
|
||||
children: [
|
||||
new Page( // page 的主要内容
|
||||
viewModel: pages[activeIndex],
|
||||
percentVisible: 1.0 ,
|
||||
),
|
||||
new PageReveal(
|
||||
revealPercent: slidePercent,
|
||||
child: new Page(
|
||||
viewModel: pages[nextPageIndex],
|
||||
percentVisible: slidePercent ,
|
||||
),
|
||||
),
|
||||
new PagerIndicator(
|
||||
viewModel: new PagerIndicatorViewModel(
|
||||
pages,
|
||||
activeIndex,
|
||||
slideDirection,
|
||||
slidePercent,
|
||||
),
|
||||
),
|
||||
new PageDragger(
|
||||
canDragLeftToRight: activeIndex > 0 ,
|
||||
canDragRightToLeft: activeIndex < pages.length - 1 ,
|
||||
slideUpdateStream: this.slideUpdateStream,
|
||||
)
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
120
lib/views/web_view_page.dart
Normal file
120
lib/views/web_view_page.dart
Normal file
@ -0,0 +1,120 @@
|
||||
/*
|
||||
* @Author: 一凨
|
||||
* @Date: 2019-01-14 17:44:47
|
||||
* @Last Modified by: 一凨
|
||||
* @Last Modified time: 2019-01-14 19:47:14
|
||||
*/
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_webview_plugin/flutter_webview_plugin.dart';
|
||||
import '../model/collection.dart';
|
||||
import '../event/event-bus.dart';
|
||||
import '../event/event-model.dart';
|
||||
import 'package:fluttertoast/fluttertoast.dart';
|
||||
|
||||
class WebViewPage extends StatefulWidget {
|
||||
final String url;
|
||||
final String title;
|
||||
|
||||
WebViewPage(this.url, this.title);
|
||||
_WebViewPageState createState() => _WebViewPageState();
|
||||
}
|
||||
|
||||
class _WebViewPageState extends State<WebViewPage> {
|
||||
bool _hasCollected = false;
|
||||
String _router = '';
|
||||
var _collectionIcons;
|
||||
CollectionControlModel _collectionControl = new CollectionControlModel();
|
||||
|
||||
void showInSnackBar(String value) {
|
||||
Fluttertoast.showToast(
|
||||
msg: value,
|
||||
toastLength: Toast.LENGTH_SHORT,
|
||||
gravity: ToastGravity.CENTER,
|
||||
timeInSecForIos: 1,
|
||||
backgroundColor: Colors.grey,
|
||||
textColor: Colors.white);
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_collectionControl.getRouterByName(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(widget.title.trim()).then((result) {
|
||||
if (result > 0 && this.mounted) {
|
||||
setState(() {
|
||||
_hasCollected = false;
|
||||
});
|
||||
showInSnackBar('已取消收藏');
|
||||
|
||||
if (ApplicationEvent.event != null) {
|
||||
ApplicationEvent.event
|
||||
.fire(CollectionEvent(widget.title, _router, true));
|
||||
}
|
||||
return;
|
||||
}
|
||||
print('删除错误');
|
||||
});
|
||||
} else {
|
||||
// 插入操作
|
||||
_collectionControl
|
||||
.insert(Collection(name: 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));
|
||||
}
|
||||
|
||||
showInSnackBar('收藏成功');
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (_hasCollected) {
|
||||
_collectionIcons = Icons.favorite;
|
||||
} else {
|
||||
_collectionIcons = Icons.favorite_border;
|
||||
}
|
||||
return WebviewScaffold(
|
||||
url: widget.url,
|
||||
appBar: AppBar(
|
||||
title: Text(widget.title),
|
||||
actions: <Widget>[
|
||||
new IconButton(
|
||||
tooltip: 'goBack home',
|
||||
onPressed: _getCollection,
|
||||
icon: Icon(_collectionIcons,),
|
||||
),
|
||||
],
|
||||
),
|
||||
withZoom: false,
|
||||
withLocalStorage: true,
|
||||
withJavascript: true,
|
||||
);
|
||||
}
|
||||
}
|
@ -96,7 +96,6 @@ class PopupMenuButtonCustom extends StatelessWidget {
|
||||
: super();
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
print('onSelected1:${widget.options}');
|
||||
final String selectStr = widget.options['defaultSelect'];
|
||||
return PopupMenuButton(
|
||||
//如果提供,则用于此按钮的widget。
|
||||
|
@ -1,9 +1,6 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
/*
|
||||
* 基本示例
|
||||
*
|
||||
*/
|
||||
// 基本示例
|
||||
class DefaultTextField extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
@ -20,7 +20,6 @@ class _MemoryImageDemoState extends State<MemoryImageDemo> {
|
||||
super.initState();
|
||||
rootBundle.load('assets/images/food01.jpeg').then((data) {
|
||||
if (mounted) {
|
||||
print(data);
|
||||
setState(() {
|
||||
bytes = data.buffer.asUint8List();
|
||||
});
|
||||
|
@ -29,6 +29,7 @@ dependencies:
|
||||
# 本地存储、收藏功能
|
||||
shared_preferences: ^0.4.3
|
||||
dio: ^1.0.6
|
||||
flutter_webview_plugin: ^0.3.0+2
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
|
Reference in New Issue
Block a user