当前位置:   article > 正文

Unity的Flutter——UIWidgets简介及入门

uiwidgets

介绍

UIWidgets(https://github.com/UnityTech/UIWidgets)是Unity编辑器的一个插件包,可帮助开发人员通过Unity引擎来创建、调试和部署高效的跨平台应用。

UIWidgets主要来自Flutter。但UIWidgets通过使用强大的Unity引擎为开发人员提供了许多新功能,显著地改进他们开发的应用性能和工作流程。

来看看效果图:

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

可以看到通过UIWidgets可以实现应用效果,UIWidgets的优势如下:

  • 效率

通过使用最新的Unity渲染SDK,UIWidgets应用可以非常快速地运行并且大多数时间保持大于60fps的速度。

  • 跨平台

与任何其他Unity项目一样,UIWidgets应用可以直接部署在各种平台上,包括PC,移动设备和网页等。

  • 多媒体支持

除了基本的2D UI之外,开发人员还能够将3D模型,音频,粒子系统添加到UIWidgets应用中。

文档

官方文档只是简单介绍,代码中没有任何中英文注释,部分demo还未完成。
不过由于基于Flutter,大部分API与Flutter一致,所以可以参考Flutter文档,或者先学习一下Flutter。

Unity Connect App

Unity Connect App是使用UIWidgets开发的一个移动App产品,可以在Android下载以及iOS App Store下载最新的版本.

github地址https://github.com/UnityTech/ConnectAppCN.

安装

首先需要 Unity 2018.3 或更高版本。

新建一个unity项目或一个已有的项目。

访问UIWidgets的github库https://github.com/UnityTech/UIWidgets下载最新UIWidgets包,将其移至项目的Package文件夹中

或者可以在终端中通过git命令来完成这个操作:

 cd <YourProjectPath>/Packages
 git clone https://github.com/UnityTech/UIWidgets.git com.unity.uiwidgets

  • 1
  • 2
  • 3

官方示例

在示例中,我们将创建一个非常简单的UIWidgets应用。 该应用只包含文本标签和按钮。 文本标签将计算按钮上的点击次数。

首先,使用Unity编辑器打开项目。

场景构建

选择 File > New Scene来创建一个新场景。

选择 GameObject > UI > Canvas
在场景中创建UI Canvas。

右键单击Canvas并选择UI > Panel,将面板添加到UI Canvas中。 然后删除面板中的 Image 组件。

最后为场景命名并保存至Assets/Scenes目录下

创建部件

创建一个新C#脚本,命名为“UIWidgetsExample.cs”

    using System.Collections.Generic;
    using Unity.UIWidgets.animation;
    using Unity.UIWidgets.engine;
    using Unity.UIWidgets.foundation;
    using Unity.UIWidgets.material;
    using Unity.UIWidgets.painting;
    using Unity.UIWidgets.ui;
    using Unity.UIWidgets.widgets;
    using UnityEngine;
    using FontStyle = Unity.UIWidgets.ui.FontStyle;

    namespace UIWidgetsSample {
        public class UIWidgetsExample : UIWidgetsPanel {
            protected override void OnEnable() {
                // if you want to use your own font or font icons.
                // FontManager.instance.addFont(Resources.Load<Font>(path: "path to your font"), "font family name");

                // load custom font with weight & style. The font weight & style corresponds to fontWeight, fontStyle of
                // a TextStyle object
                // FontManager.instance.addFont(Resources.Load<Font>(path: "path to your font"), "Roboto", FontWeight.w500,
                //    FontStyle.italic);

                // add material icons, familyName must be "Material Icons"
                // FontManager.instance.addFont(Resources.Load<Font>(path: "path to material icons"), "Material Icons");

                base.OnEnable();
            }

            protected override Widget createWidget() {
                return new WidgetsApp(
                    home: new ExampleApp(),
                    pageRouteBuilder: (RouteSettings settings, WidgetBuilder builder) =>
                        new PageRouteBuilder(
                            settings: settings,
                            pageBuilder: (BuildContext context, Animation<float> animation,
                                Animation<float> secondaryAnimation) => builder(context)
                        )
                );
            }

            class ExampleApp : StatefulWidget {
                public ExampleApp(Key key = null) : base(key) {
                }

                public override State createState() {
                    return new ExampleState();
                }
            }

            class ExampleState : State<ExampleApp> {
                int counter = 0;

                public override Widget build(BuildContext context) {
                    return new Column(
                        children: new List<Widget> {
                            new Text("Counter: " + this.counter),
                            new GestureDetector(
                                onTap: () => {
                                    
                    //这里使用setState来改变counter的值则可以同步改变Text显示,如果不用直接counter++则无法改变显示                this.setState(() => {
                                        this.counter++;
                                    });
                                },
                                child: new Container(
                                    padding: EdgeInsets.symmetric(20, 20),
                                    color: Colors.blue,
                                    child: new Text("Click Me")
                                )
                            )
                        }
                    );
                }
            }
        }
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75

保存脚本,并附加到panel中作为其组件。
(如果添加失败,请检查文件名与类名是否一致)

保存场景,运行就可以看到效果了。

Image组件

简单介绍一下Image组件

加载资源文件

将资源图片放入Assets/Resources目录下

使用asset函数即可创建一个Image并加载相应资源

Unity.UIWidgets.widgets.Image.asset("test")
  • 1

注意不需要文件后缀。

加载网络资源

Unity.UIWidgets.widgets.Image.network("https://www.baidu.com/img/xinshouyedong_4f93b2577f07c164ae8efa0412dd6808.gif")
  • 1

Image支持Gif!可以直接加载显示Gif。

除了上面两种方式,还可以通过文件和byte数组来加载资源,函数分别为file()和memory()。

改变大小等属性

Unity.UIWidgets.widgets.Image.asset(
  name: "test",
  height: 100
)
  • 1
  • 2
  • 3
  • 4

这里涉及到默认参数,先来看一个aseet函数源码

public static Image asset(
            string name,
            Key key = null,
            AssetBundle bundle = null,
            float? scale = null,
            float? width = null,
            float? height = null,
            Color color = null,
            BlendMode colorBlendMode = BlendMode.srcIn,
            BoxFit? fit = null,
            Alignment alignment = null,
            ImageRepeat repeat = ImageRepeat.noRepeat,
            Rect centerSlice = null,
            bool gaplessPlayback = false,
            FilterMode filterMode = FilterMode.Bilinear
        ) {
            var image = scale != null
                ? (AssetBundleImageProvider) new ExactAssetImage(name, bundle: bundle, scale: scale.Value)
                : new AssetImage(name, bundle: bundle);

            return new Image(
                key,
                image,
                width,
                height,
                color,
                colorBlendMode,
                fit,
                alignment,
                repeat,
                centerSlice,
                gaplessPlayback,
                filterMode
            );
        }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35

除了name,其他参数都设置了默认参数,这样在使用这个函数时,无需改变默认参数的参数不必传入,这样就需要传參时带上参数名。但是如果只传一个name参数的时候,可以省略参数名。

所以想改变或设置Image或其他组件的属性时,只需要添加对应参数即可。

如改变图片的拉伸规则

Unity.UIWidgets.widgets.Image.asset(
   name: "test",
   height: 100,
   width: 100,
   fit: Unity.UIWidgets.painting.BoxFit.fill
)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

Navigation页面跳转

参考官方示例中的demo,简化代码如下:

using System;
using System.Collections.Generic;
using Unity.UIWidgets.animation;
using Unity.UIWidgets.engine;
using Unity.UIWidgets.foundation;
using Unity.UIWidgets.gestures;
using Unity.UIWidgets.painting;
using Unity.UIWidgets.rendering;
using Unity.UIWidgets.ui;
using Unity.UIWidgets.widgets;
using TextStyle = Unity.UIWidgets.painting.TextStyle;

namespace UIWidgetsSample {
   public class NavigationEx : UIWidgetsPanel{

       protected override Widget createWidget() {
           return new WidgetsApp(
               initialRoute: "/",
               textStyle: new TextStyle(fontSize: 24),
               pageRouteBuilder: this.pageRouteBuilder,
               //初始化所有route路由
               routes: new Dictionary<string, WidgetBuilder> {
                   {"/", (context) => new HomeScreen()},
                   {"/detail", (context) => new DetailScreen()}
               });
       }
       
       protected PageRouteFactory pageRouteBuilder {
           get {
               return (RouteSettings settings, WidgetBuilder builder) =>
                   new PageRouteBuilder(
                       settings: settings,
                       pageBuilder: (BuildContext context, Animation<float> animation,
                           Animation<float> secondaryAnimation) => builder(context),
                       //设置转场动画,去掉则没有转场动画
                       transitionsBuilder: (BuildContext context, Animation<float>
                               animation, Animation<float> secondaryAnimation, Widget child) =>
                           new _FadeUpwardsPageTransition(
                               routeAnimation: animation,
                               child: child
                           )
                   );
           }
       }
   }

   //首页
   class HomeScreen : StatelessWidget {
       public override Widget build(BuildContext context) {
           //封装了一个NavigationPage,详细见后续代码
           return new NavigationPage(
               body: new Container(
                   //设置背景色
                   color: new Color(0xFF888888),
                   //Center组件可以实现相对parent居中
                   child: new Center(
                       //设置按钮点击后跳转到“/detail”
                       child: new GestureDetector(onTap: () => { Navigator.pushNamed(context, "/detail"); },
                           child: new Text("Go to Detail"))
                   )),
               title: "Home"
           );
       }
   }

   //详情页
   class DetailScreen : StatelessWidget {
       public override Widget build(BuildContext context) {
           return new NavigationPage(
               body: new Container(
                   color: new Color(0xFF1389FD),
                   child: new Center(
                       child: new Column(
                           children: new List<Widget>() {
                               //设置按钮点击关闭页面,回到上一页
                               new GestureDetector(onTap: () => { Navigator.pop(context); }, child: new Text("Back"))
                           }
                       )
                   )),
               title: "Detail");
       }
   }

   //转场动画
   class _FadeUpwardsPageTransition : StatelessWidget {
       internal _FadeUpwardsPageTransition(
           Key key = null,
           Animation<float> routeAnimation = null, // The route's linear 0.0 - 1.0 animation.
           Widget child = null
       ) : base(key: key) {
           //设置滑动的偏移动画,并且添加了动画曲线fastOutSlowIn,具体见后面代码
           this._positionAnimation = _bottomUpTween.chain(_fastOutSlowInTween).animate(routeAnimation);
           //设置显隐动画,并且添加了动画曲线easeIn,具体见后面代码
           this._opacityAnimation = _easeInTween.animate(routeAnimation);
           this.child = child;
       }

       //设置(滑动)动画的x、y偏移,是百分比值。开始点是x无偏移,y偏移0.25,即从页面高度的1/4开始。结束点是无偏移,即原位置。所以动画是从页面1/4处向上滑动到顶部
       static Tween<Offset> _bottomUpTween = new OffsetTween(
           begin: new Offset(0.0f, 0.25f),
           end: Offset.zero
       );

       //动画曲线fastOutSlowIn,先加速再减速
       static Animatable<float> _fastOutSlowInTween = new CurveTween(curve: Curves.fastOutSlowIn);
       //动画曲线easeIn,初始缓动
       static Animatable<float> _easeInTween = new CurveTween(curve: Curves.easeIn);

       readonly Animation<Offset> _positionAnimation;
       readonly Animation<float> _opacityAnimation;
       public readonly Widget child;

       public override Widget build(BuildContext context) {
           //转场动画是包含一个滑动动画和一个显隐动画,是一个层次关系
           return new SlideTransition(
               position: this._positionAnimation,
               child: new FadeTransition(
                   opacity: this._opacityAnimation,
                   //需要实现动画的组件
                   child: this.child
               )
           );
       }
   }

   //封装了一个导航布局,顶部导航栏,底部页面内容
   class NavigationPage : StatelessWidget {
       //页面内容
       public readonly Widget body;
       //页面标题,显示在导航栏
       public readonly string title;

       public NavigationPage(Widget body = null, string title = null) {
           this.title = title;
           this.body = body;
       }

       public override Widget build(BuildContext context) {
           Widget back = null;
           //判断是否可以回退页面,即页面列表的size大于1。如果可以则创建back按钮,点击关闭当前页回退到上一页
           if (Navigator.of(context).canPop()) {
               back = new GestureDetector(onTap: () => { Navigator.pop(context); },
                   child: new Text("Go Back"));
               back = new Column(mainAxisAlignment: MainAxisAlignment.center, children: new List<Widget>() {back});
           }


           return new Container(
               child: new Column(
                   children: new List<Widget>() {
                       //顶部是一个导航栏,这里可以看到用了ConstrainedBox和DecoratedBox
                       new ConstrainedBox(constraints: new BoxConstraints(maxHeight: 80),
                           child: new DecoratedBox(
                               decoration: new BoxDecoration(color: new Color(0XFFE1ECF4)),
                               //NavigationToolbar导航工具栏,其中分为三个区域:左侧leading、中间middle和右侧trailing。这里左侧放置了回退按钮
                               child: new NavigationToolbar(leading: back,
                                   middle: new Text(this.title, textAlign: TextAlign.center)))),
                       //内容部分。这里使用了一个Flexible,作用是可以填满剩余的空间,如果使用普通的container无法达到效果
                       new Flexible(child: this.body)
                   }
               )
           );
       }
   }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165

可以看到在创建WidgetsApp时,设定所有路由的路径和实现,然后通过Navigator类切换路径来控制页面跳转。

Material

UIWidgets同样提供了Material风格的组件,通过一个demo简单认识一下

using System.Collections.Generic;
using Unity.UIWidgets.animation;
using Unity.UIWidgets.engine;
using Unity.UIWidgets.material;
using Unity.UIWidgets.painting;
using Unity.UIWidgets.rendering;
using Unity.UIWidgets.service;
using Unity.UIWidgets.ui;
using Unity.UIWidgets.widgets;
using UnityEngine;
using Image = Unity.UIWidgets.widgets.Image;

namespace UIWidgetsEx {
   public class MaterialEx : UIWidgetsPanel{

       protected override Widget createWidget()
       {
           return new MaterialApp(
               home: new MaterialThemeSampleWidget(),
               darkTheme: new ThemeData(primaryColor: Colors.black26)
           );
       }

       protected override void OnEnable()
       {
           //加载Material字体,用于icon
           FontManager.instance.addFont(Resources.Load<Font>(path: "MaterialIcons-Regular"), "Material Icons");
           base.OnEnable();
       }
   }

   public class MaterialThemeSampleWidget : StatefulWidget
   {
       public override State createState()
       {
           return new _MaterialThemeSampleWidgetState();
       }
   }

   class _MaterialThemeSampleWidgetState : State<MaterialThemeSampleWidget>
   {
       //GlobalKey<ScaffoldState> scaffoldKey = GlobalKey<ScaffoldState>.key("lalala");
       public override Widget build(BuildContext context)
       {
           return new Theme(
               //data设置样式
               data: new ThemeData(
                   appBarTheme: new AppBarTheme(
                       color: Colors.purple
                   ),
                   bottomAppBarTheme: new BottomAppBarTheme(
                       color: Colors.blue
                   ),
                   cardTheme: new CardTheme(
                       color: Colors.red,
                       //设置深度,即阴影大小
                       elevation: 2.0f
                   )
               ),
               //页面根布局是一个Scaffold,大致分为三个区域appBar、body和bottomNavigationBar。
               child: new Scaffold(
                   //key: scaffoldKey,
                   //导航栏AppBar
                   appBar: new AppBar(
                       //设置深度,即阴影大小
                       //elevation: 25f,
                       title: new Text("Test App Bar Theme")),
                   body: new Center(
                       //组件Card,自带阴影效果
                       child: new Card(
                           //设置深度,即阴影大小
                           //elevation: 25f,
                           //设置圆角borderRadius,还可以设置边框side
                           shape: new RoundedRectangleBorder(
                           //side: new BorderSide(Colors.blue, 5.0f),
                               borderRadius: BorderRadius.all(5.0f)
                           ),
                           child: new Container(
                               height: 250,
                               child: new Column(
                                   children: new List<Widget> {
                                       Image.asset(
                                           "products/backpack",
                                           fit: BoxFit.cover,
                                           width: 200,
                                           height: 200
                                       ),
                                       new Text("Card Theme")
                                   }
                               )
                           )
                       )
                   ),
                   //底部是BottomAppBar
                   bottomNavigationBar: new BottomAppBar(
                       child: new Row(
                       mainAxisSize: MainAxisSize.max,
                       //设置对齐方式
                       mainAxisAlignment: MainAxisAlignment.spaceBetween,
                       children: new List<Widget> {
                           //底部是两个IconButton,使用字体中的icon
                           new IconButton(icon: new Icon(Unity.UIWidgets.material.Icons.menu), onPressed: () => { }),
                           new IconButton(icon: new Icon(Unity.UIWidgets.material.Icons.account_balance), onPressed: () => { })
                       })
                   )
                    //,
                   //floatingActionButton: new FloatingActionButton(
                   //    child: new Text("go"),
                   //    onPressed: () => {
                   //        scaffoldKey.currentState.showSnackBar(
                   //            new SnackBar(
                   //                content: new Text("go")
                   //            )
                   //        );
                   //    }
                   //),
                   //floatingActionButtonLocation: FloatingActionButtonLocation.endDocked
               )
           );
       }
   }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122

有关Material部分跟Android基本一致,比如组件都有深度属性elevation,比如Card就是Material中的CardView。

IconButtom使用字体Font,实际上就是5.0之后引入的SVG,即Vector Image

我们简单补充一下:

Scaffold

实际上对应着Android中的CoordinateLayout,分为三大区域appBar、body和bottomNavigationBar,当未设置时则隐藏该区域,body会补充填充。

除了三大区域还包括:悬浮按钮floatingActionButton、bottomSheet、drawer、endDrawer等

其中floatingActionButton同样保持着android中的behavior效果,下面会详细介绍

AppBar

AppBar则包含:title、左侧leading、右侧扩展菜单actions

MainAxisAlignment

对齐方式,这个对齐方式相对于android更灵活一些,包括:

  • start 左(上)侧对齐
  • end 右(下)侧对齐
  • center 居中对齐
  • spaceBetween 每两个item的间隔space相同,两侧的item与父布局没有间隔
  • spaceAround 每个item四周都有同样的间隔,相当于margin,所以两个item之间是两倍间隔
  • spaceEvenly item在父布局中均匀分布,即所有间隔相同

FloatingActionButton

在Scaffold中floatingActionButton和floatingActionButtonLocation要搭配使用,floatingActionButtonLocation决定着按钮的位置,包含:

  • endFloat: body的右下角
  • endDocked: bottomNavigationBar右侧,中心锚点在bottomNavigationBar的顶端
  • centerDocked: bottomNavigationBar中间,中心锚点在bottomNavigationBar的顶端
  • centerFloat: body的底部中间
  • startTop:appBar左侧,中心锚点appBar的底端
  • miniStartTop:与startTop类似,与左侧间隔更小一点
  • endTop:appBar右侧,中心锚点appBar的底端

除了上面这些我们还可以实现FloatingActionButtonLocation这个抽象类自定义位置

SnackBar

在Android中FloatingActionButton是存在一个默认behavior的,最明显的就是与SnackBar互动。

在UIWidgets中也有SnackBar组件,使用起来比较简单。

首先需要创建一个GlobalKey,并将它赋给Scaffold的key。这样我们就可以使用
scaffoldKey.currentState.showSnackBar()这个函数来显示一个SnackBar。

Animation

在前面的示例中我们使用了转场动画,但是并没有体现动画的完整应用,因为部分逻辑比如执行动画等封装在底层了。下面这个例子将完整的展现如何创建并执行一个动画。

using System.Collections.Generic;
using Unity.UIWidgets.animation;
using Unity.UIWidgets.cupertino;
using Unity.UIWidgets.engine;
using Unity.UIWidgets.foundation;
using Unity.UIWidgets.scheduler;
using Unity.UIWidgets.ui;
using Unity.UIWidgets.widgets;

namespace UIWidgetsSample
{
    public class AnimEx : UIWidgetsPanel
    {
        protected override void OnEnable()
        {
            base.OnEnable();
        }

        protected override Widget createWidget()
        {
            return new WidgetsApp(
                home: new ExampleApp(),
                pageRouteBuilder: (RouteSettings settings, WidgetBuilder builder) =>
                    new PageRouteBuilder(
                        settings: settings,
                        pageBuilder: (BuildContext context, Animation<float> animation,
                            Animation<float> secondaryAnimation) => builder(context)
                    )
            );
        }

        class ExampleApp : StatefulWidget
        {
            public ExampleApp(Key key = null) : base(key)
            {
            }

            public override State createState()
            {
                return new ExampleState();
            }
        }

        class ExampleState : State<ExampleApp>
        {
            //IconData iconData = Unity.UIWidgets.material.Icons.menu;
            //Curve switchCurve = new Interval(0.4f, 1.0f, curve: Curves.fastOutSlowIn);
            //TextAnim类用一个动画组件将Image组件封装起来,见后面
            TextAnim textAnim = new TextAnim();
            public override Widget build(BuildContext context)
            {
                return new Column(
                    children: new List<Widget> {
                        切换动画
                        //new AnimatedSwitcher(
                        //    duration: new System.TimeSpan(0, 0, 1),
                        //    switchInCurve: switchCurve,
                        //    switchOutCurve: switchCurve,
                        //    child: new IconButton(
                        //        //不同的key才会认为是不同的组件,否则不会执行动画
                        //        key: new ValueKey<IconData>(iconData),
                        //        icon: new Icon(icon :iconData, color: Colors.white),
                        //        onPressed: () => {
                        //            this.setState(() => {
                        //                if (iconData.Equals(Unity.UIWidgets.material.Icons.menu))
                        //                {
                        //                    iconData = Unity.UIWidgets.material.Icons.close;
                        //                }
                        //                else
                        //                {
                        //                    iconData = Unity.UIWidgets.material.Icons.menu;
                        //                }
                        //            });
                        //        }
                        //    )
                        //),
                        //这里使用了Cupertino风格的button
                        new CupertinoButton(onPressed: () => {
                                //点击后通过controller执行动画
                                textAnim.controller.forward(); 
                            },
                            child: new Text("Go"),
                            color: CupertinoColors.activeBlue
                            ),
                            textAnim.build(context)
                    }
                   );
            }
        }

        //继承SingleTickerProviderStateMixin,这是TickerProvider接口的实现类
        private class TextAnim : SingleTickerProviderStateMixin<ExampleApp>
        {
            public AnimationController controller = null;
            public override Widget build(BuildContext context)
            {
                //定一个一个AnimationController。TimeSpan(0, 0, 2)代表0小时0分2秒,也就是动画时长是两秒
                controller = new AnimationController(duration: new System.TimeSpan(0, 0, 2), vsync: this);
                
                //设置动画监听,当结束时还原
                controller.addStatusListener((status) => {
                    if(status == AnimationStatus.completed)
                    {
                        controller.reset();
                    }
                });
                
                //定义Animation,从原点下移自身两个高度
                Animation<Offset> offset = controller.drive(new OffsetTween(
                    begin: Offset.zero,
                    end: new Offset(0.0f, 2f)
                ));
                
                //创建一个滑动动画组件
                return new SlideTransition(
                            position: offset,
                            child: Unity.UIWidgets.widgets.Image.asset(
                                name: "test",
                                height: 100
                            )
                        );
            }
        }
    }

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126

执行后点击button,图片会执行下移动画,执行完回到原位置。

当然我们还可以设置动画曲线、组合多个动画等等。

而且我们可以看到使用了ios的Cupertino风格的button。与Material一样,UIWidgets也有一套Cupertino风格的组件。

vsync

创建AnimationController时,需要设置一个vsync,是TickerProvider类型的,即计时器。那么这个东西到底有什么用?

vsync对象会绑定动画的定时器到一个可视的widget,所以当widget不显示时,动画定时器将会暂停,当widget再次显示时,动画定时器重新恢复执行,这样就可以避免动画相关UI不在当前屏幕时消耗资源。

AnimatedSwitcher

切换动画,可以同时对其新、旧子元素添加显示、隐藏动画。新旧子元素可以是是两个组件,也可以利用key将一个组件同时用于新旧子元素。

总结

通过上面介绍可以看到,UIWidgets实际上就是Unity上的Flutter,使用起来与Flutter也非常类似。关于Flutter我推荐一下自己写的几篇文章:
【Flutter混合开发】在Android项目中如何启动Flutter
【Flutter进阶】与Native进行交互的三种方式
【Flutter进阶】聊一聊组件中的生命周期、状态管理及局部重绘

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/AllinToyou/article/detail/82901
推荐阅读
相关标签
  

闽ICP备14008679号