为Flutter应用程序添加交互性

你会学到什么:html

  • 如何响应信号。
  • 如何建立自定义小部件。
  • 无状态和有状态小部件之间的区别。

你如何修改你的应用程序,使其对用户输入作出反应? 在本教程中,您将为仅包含非交互式小部件的应用添加交互性。 具体来讲,您将经过建立一个管理两个无状态小部件的自定义状态小部件来修改图标以使其能够点击。java

内容

有状态和无状态的小部件
建立一个有状态的小部件ios

  • 第1步:决定哪一个对象管理小部件的状态
  • 第2步:子类StatefulWidget
  • 第3步:子类状态
  • 第4步:将有状态小部件插入小部件树中
  • 问题?

管理状态git

  • 小部件管理本身的状态
  • 父母管理小部件的状态
  • 混搭方法

其余交互式小部件github

  • 标准小部件
  • 材料组件

资源web

准备好

若是您已经在Flutter布局中构建布局,请跳到下一节。macos

确保你已经创建了你的环境。app

一旦你有一个链接和启用的设备,或者你已经启动了iOS模拟器(Flutter安装的一部分),你很好!框架

Flutter的Building Layouts展现了如何为下面的截图建立布局。less

当应用第一次启动时,这颗恒星是纯红色的,代表这个湖之前已经被收藏了。 星号旁边的数字表示41我的对此湖感兴趣。 完成本教程后,轻敲星星将删除其偏好状态,用轮廓线代替实心星并减小计数。 再次轻拍湖面,画出星星并增长计数。

为了实现这一点,您将建立一个包含星号和计数的自定义小部件,它们都是小部件。 由于点击明星会更改这两个小部件的状态,因此同一个小部件应该同时管理这两个小部件。

您能够正确触摸第2步:子类StatefulWidget中的代码。 若是您想尝试不一样方式管理状态,请跳至管理状态

有状态和无状态的小部件

重点是什么?

  • 有些小部件是有状态的,有些是无状态的。
  • 若是一个小部件发生变化 - 用户与它进行交互,例如 - 它是有状态的。
  • 小部件的状态由能够改变的值组成,例如滑块的当前值或复选框是否被选中。
  • 小部件的状态存储在状态对象中,从而将小部件的状态与外观分开。
  • 当小部件的状态改变时,状态对象调用setState(),告诉框架重绘小部件。

无状态小部件没有内部状态来管理。 IconIconButtonTextStatelessWidget子类的无状态小部件示例。

有状态的小部件是动态的。 用户能够与有状态的小部件进行交互(例如经过输入表单或移动滑块),或者随着时间的推移而变化(多是数据馈送致使UI更新)。 CheckboxRadioSliderInkWellFormTextFieldStatefulWidget子类的有状态小部件的示例。

建立一个有状态的小部件

重点是什么?

  • 要建立一个自定义状态小部件,能够建立两个类:StatefulWidget和State。
  • 状态对象包含小部件的状态和小部件的build()方法。
  • 当小部件的状态改变时,状态对象调用setState(),告诉框架重绘小部件。

在本节中,您将建立一个自定义有状态小部件。 您将使用一个自定义状态小部件替换两个无状态小部件 - 纯红星和其旁边的数字计数 - 该小部件用两个子部件管理一行:IconButton和Text。

实现一个定制的有状态小部件须要建立两个类:

  • 定义小部件的StatefulWidget的子类。
  • State的一个子类,它包含该小部件的状态并定义小部件的build()方法。

本节展现如何为Lakes应用程序构建一个名为Favorite Widget的有状态小部件。 第一步是选择如何管理Favorite Widgets状态。

第1步:决定哪一个对象管理小部件的状态

小部件的状态能够经过多种方式进行管理,但在咱们的示例中,小部件自己(FavoriteWidget)将管理本身的状态。 在这个例子中,切换星号是一个独立的操做,不会影响父窗口小部件或其余用户界面,所以窗口小部件能够在内部处理它的状态。

在管理状态中了解更多关于窗口小部件和状态的分离以及如何管理状态的信息。

第2步:子类StatefulWidget

FavoriteWidget类管理本身的状态,所以它重写createState()来建立状态对象。 当它想要构建小部件时,框架调用createState()。 在这个例子中,createState()建立_FavoriteWidgetState的一个实例,你将在下一步中实现它。

class FavoriteWidget extends StatefulWidget {
  @override
  _FavoriteWidgetState createState() => new _FavoriteWidgetState();
}

注意:如下划线(_)开头的成员或类是私有的。 有关更多信息,请参阅Dart语言参考中的库和可见性部分。

第3步:子类状态

自定义State类存储可变信息 - 能够在小部件的生命周期内改变的逻辑和内部状态。 当应用第一次启动时,用户界面显示一个稳固的红色星星,代表该湖有“最喜欢”的状态,并有41个“喜欢”。 状态对象将这些信息存储在_isFavorited_favoriteCount变量中。

状态对象还定义了build方法。 此build方法建立一个包含红色IconButton和Text的行。 该小部件使用IconButton(而不是Icon),由于它有一个onPressed属性,该属性定义了处理水龙头的回调方法。 IconButton也有一个保存图标的Icon属性。

_toggleFavorite()方法在按下IconButton时调用,它调用setState()。 调用setState()是相当重要的,由于这会告诉框架小部件的状态已经改变,而且小部件应该重绘。 _toggleFavorite函数在1)星形图标和数字“41”,以及2)star_border图标和数字“40”之间交换UI。

class _FavoriteWidgetState extends State<FavoriteWidget> {
  bool _isFavorited = true;
  int _favoriteCount = 41;

  void _toggleFavorite() {
    setState(() {
      // If the lake is currently favorited, unfavorite it.
      if (_isFavorited) {
        _favoriteCount -= 1;
        _isFavorited = false;
        // Otherwise, favorite it.
      } else {
        _favoriteCount += 1;
        _isFavorited = true;
      }
    });
  }

  @override
  Widget build(BuildContext context) {
    return new Row(
      mainAxisSize: MainAxisSize.min,
      children: [
        new Container(
          padding: new EdgeInsets.all(0.0),
          child: new IconButton(
            icon: (_isFavorited
                ? new Icon(Icons.star)
                : new Icon(Icons.star_border)),
            color: Colors.red[500],
            onPressed: _toggleFavorite,
          ),
        ),
        new SizedBox(
          width: 18.0,
          child: new Container(
            child: new Text('$_favoriteCount'),
          ),
        ),
      ],
    );
  }
}

提示:将文本放置在SizedBox中并设置其宽度可防止文本在40和41之间变化时出现明显的“跳跃” - 不然会发生这种状况,由于这些值具备不一样的宽度。

第4步:将有状态小部件插入小部件树中

将您的自定义状态小部件添加到应用构建方法中的小部件树中。 首先,找到建立图标和文本的代码,并删除它:

// ...
new Icon(
  Icons.star,
  color: Colors.red[500],
),
new Text('41')
// ...

在相同的位置建立有状态的小部件:

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    Widget titleSection = new Container(
      // ...
      child: new Row(
        children: [
          new Expanded(
            child: new Column(
              // ...
          ),
          new FavoriteWidget(),
        ],
      ),
    );

    return new MaterialApp(
      // ...
    );
  }
}

而已! 当您从新加载应用程序时,星形图标如今应该响应点击。

问题?

若是您没法运行代码,请在IDE中查找可能的错误。 调试Flutter应用程序可能会有所帮助。 若是仍然没法找到问题,请根据GitHub上的交互式湖区示例检查代码。

若是您仍然有疑问,请参阅获取支持

本页面的其他部分介绍了能够管理窗口小部件状态的几种方式,并列出了其余可用的交互窗口小部件。

管理状态

重点是什么?

  • 管理状态有不一样的方法。
  • 您做为小部件设计师,选择使用哪一种方法。
  • 若是有疑问,首先管理父窗口小部件中的状态。

谁管理有状态小部件的状态? 小部件自己? 父窗口小部件? 都? 另外一个对象? 答案是......这取决于依赖高关系。有几种有效的方法可让你的小部件互动。做为小部件设计师,您根据您指望使用的小部件作出决定。如下是管理状态的最多见方法:

你如何决定使用哪一种方法? 如下原则能够帮助您决定:

  • 若是有问题的状态是用户数据,例如复选框的选中或未选中模式或滑块位置,则该状态最好由父控件管理。
  • 若是所讨论的状态是审美的,例如动画,那么状态最好由小部件自己来管理。

若是有疑问,首先管理父窗口小部件中的状态。

咱们将经过建立三个简单示例来举例说明管理状态的不一样方式:TapboxA,TapboxB和TapboxC。 这些例子都是相似的工做 - 每建立一个容器,当点击时,在绿色或灰色框之间切换。 _active布尔值肯定颜色:绿色表示激活或者灰色表示不激活。

这些示例使用GestureDetector捕获Container上的活动。

小部件管理本身的状态

有时,小部件在内部管理其状态是最有意义的。 例如,当ListView的内容超过渲染框时,ListView自动滚动。 大多数使用ListView的开发人员不想管理ListView的滚动行为,所以ListView自己管理其滚动偏移量。

_TapboxAState类:

  • 管理TapboxA的状态。
  • 定义_active布尔值决定框的当前颜色。
  • 定义_handleTap()函数,轻击框时该函数更新_active,并调用setState()函数来更新UI。
  • 实现小部件的全部交互式行为。
// TapboxA manages its own state.

//------------------------- TapboxA ----------------------------------

class TapboxA extends StatefulWidget {
  TapboxA({Key key}) : super(key: key);

  @override
  _TapboxAState createState() => new _TapboxAState();
}

class _TapboxAState extends State<TapboxA> {
  bool _active = false;

  void _handleTap() {
    setState(() {
      _active = !_active;
    });
  }

  Widget build(BuildContext context) {
    return new GestureDetector(
      onTap: _handleTap,
      child: new Container(
        child: new Center(
          child: new Text(
            _active ? 'Active' : 'Inactive',
            style: new TextStyle(fontSize: 32.0, color: Colors.white),
          ),
        ),
        width: 200.0,
        height: 200.0,
        decoration: new BoxDecoration(
          color: _active ? Colors.lightGreen[700] : Colors.grey[600],
        ),
      ),
    );
  }
}

//------------------------- MyApp ----------------------------------

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      title: 'Flutter Demo',
      home: new Scaffold(
        appBar: new AppBar(
          title: new Text('Flutter Demo'),
        ),
        body: new Center(
          child: new TapboxA(),
        ),
      ),
    );
  }
}

Dart代码:lib/main.dart

父部件管理小部件的状态

对于父窗口小部件来讲,管理状态并告诉其子窗口小部件什么时候更新一般是最有意义的。 例如,IconButton容许您将图标视为可点按的按钮。 IconButton是一个无状态的小部件,由于咱们认为父部件须要知道该按钮是否已被轻敲,因此它能够采起适当的行动。

在如下示例中,TapboxB经过回调将其状态导出到其父项。 因为TapboxB无论理任何状态,所以它的子类为无状态部件。

ParentWidgetState类:

  • 管理TapboxB的_active状态。
  • 实现_handleTapboxChanged(),当方块被点击时调用该方法。
  • 当状态改变时,调用setState()来更新UI。

TapboxB类:

  • 扩展StatelessWidget,由于全部状态都由其父级处理。
  • 当检测到轻击时,它会通知父母。
// ParentWidget manages the state for TapboxB.

//------------------------ ParentWidget --------------------------------

class ParentWidget extends StatefulWidget {
  @override
  _ParentWidgetState createState() => new _ParentWidgetState();
}

class _ParentWidgetState extends State<ParentWidget> {
  bool _active = false;

  void _handleTapboxChanged(bool newValue) {
    setState(() {
      _active = newValue;
    });
  }

  @override
  Widget build(BuildContext context) {
    return new Container(
      child: new TapboxB(
        active: _active,
        onChanged: _handleTapboxChanged,
      ),
    );
  }
}

//------------------------- TapboxB ----------------------------------

class TapboxB extends StatelessWidget {
  TapboxB({Key key, this.active: false, @required this.onChanged})
      : super(key: key);

  final bool active;
  final ValueChanged<bool> onChanged;

  void _handleTap() {
    onChanged(!active);
  }

  Widget build(BuildContext context) {
    return new GestureDetector(
      onTap: _handleTap,
      child: new Container(
        child: new Center(
          child: new Text(
            active ? 'Active' : 'Inactive',
            style: new TextStyle(fontSize: 32.0, color: Colors.white),
          ),
        ),
        width: 200.0,
        height: 200.0,
        decoration: new BoxDecoration(
          color: active ? Colors.lightGreen[700] : Colors.grey[600],
        ),
      ),
    );
  }
}

Dart代码:lib/main.dart

提示:建立API时,请考虑对代码所依赖的任何参数使用@required注释。 要使用@required,请导入foundation库(该库从新导出Dart的meta.dart库):

import ”package:flutter/foundation.dart“;

混搭方法

对于一些小部件来讲,混合搭配的方法最有意义。 在这种状况下,有状态小部件管理一些状态,而且父小部件管理状态的其它方面。

在TapboxC示例中,按下时,框的周围会出现一个深绿色的边框。 抬起时,边框消失,框的颜色改变。 TapboxC将其_active状态导出到其父项,但在内部管理_highlight状态。 这个例子有两个状态对象_ParentWidgetState和_TapboxCState。

_ParentWidgetState对象:

  • 管理_active状态。
  • 实现_handleTapboxChanged(),当方块被点击时调用该方法。
  • 调用setState()以在发生轻击和_active状态改变时更新UI。

_TapboxCState对象:

  • 管理_highlight状态。
  • GestureDetector监听全部轻击事件。 随着用户点击,它添加高亮(实现为深绿色边框)。 当用户释放水龙头时,它会消除高光。
  • 按下时,抬起或点击取消调用setState()更新界面而且_highlight状态改变。
  • 在点击事件中,将该状态更改传递给父部件,以使用widget属性采起适当的操做。
//---------------------------- ParentWidget ----------------------------

class ParentWidget extends StatefulWidget {
  @override
  _ParentWidgetState createState() => new _ParentWidgetState();
}

class _ParentWidgetState extends State<ParentWidget> {
  bool _active = false;

  void _handleTapboxChanged(bool newValue) {
    setState(() {
      _active = newValue;
    });
  }

  @override
  Widget build(BuildContext context) {
    return new Container(
      child: new TapboxC(
        active: _active,
        onChanged: _handleTapboxChanged,
      ),
    );
  }
}

//----------------------------- TapboxC ------------------------------

class TapboxC extends StatefulWidget {
  TapboxC({Key key, this.active: false, @required this.onChanged})
      : super(key: key);

  final bool active;
  final ValueChanged<bool> onChanged;

  _TapboxCState createState() => new _TapboxCState();
}

class _TapboxCState extends State<TapboxC> {
  bool _highlight = false;

  void _handleTapDown(TapDownDetails details) {
    setState(() {
      _highlight = true;
    });
  }

  void _handleTapUp(TapUpDetails details) {
    setState(() {
      _highlight = false;
    });
  }

  void _handleTapCancel() {
    setState(() {
      _highlight = false;
    });
  }

  void _handleTap() {
    widget.onChanged(!widget.active);
  }

  Widget build(BuildContext context) {
    // This example adds a green border on tap down.
    // On tap up, the square changes to the opposite state.
    return new GestureDetector(
      onTapDown: _handleTapDown, // Handle the tap events in the order that
      onTapUp: _handleTapUp, // they occur: down, up, tap, cancel
      onTap: _handleTap,
      onTapCancel: _handleTapCancel,
      child: new Container(
        child: new Center(
          child: new Text(widget.active ? 'Active' : 'Inactive',
              style: new TextStyle(fontSize: 32.0, color: Colors.white)),
        ),
        width: 200.0,
        height: 200.0,
        decoration: new BoxDecoration(
          color:
              widget.active ? Colors.lightGreen[700] : Colors.grey[600],
          border: _highlight
              ? new Border.all(
                  color: Colors.teal[700],
                  width: 10.0,
                )
              : null,
        ),
      ),
    );
  }
}

备用实现可能会将高亮状态导出到父级,同时在内部保持活动状态,但若是你问某人使用那个水龙头盒,他们可能会抱怨说这没有什么意义。 开发人员会关心该框是否处于活动状态。开发人员可能不在意突出显示是如何管理的,而且倾向于轻敲框处理这些细节。

Dart代码:lib/main.dart

其它交互式小部件

Flutter提供各类按钮和相似的交互式小部件。 这些小部件中的大多数实现了Material Design指南,它们定义了一组具备自认UI的组件。

若是你愿意,你可使用GestureDetector来创建任何自定义小部件的交互性。 您能够在管理状态Flutter图库中找到GestureDetector的示例。

注意:Flutter还提供了一组名为Cupertino的iOS风格的小部件。

当你须要交互性时,最容易使用预制的小部件之一。 这是一个部分列表:

标准小部件:

材料组件:

资源

将交互添加到您的应用时,如下资源可能会有所帮助。

相关文章
相关标签/搜索