小部件从Flutter屏幕外滑动?类似于Android 8应用抽屉

时间:2018-07-22 03:47:54

标签: flutter flutter-layout

我正在Flutter中编写抽认卡应用程序(对开源AnkiDroid应用程序的扩展)。基本的工作流程是:该应用程序向我显示一个问题,我可以显示答案。我想要显示答案的手势类似于从底部图标行向上滑动以显示应用程序抽屉的Android 8。快速滑动(或用Android术语猛击?)可以显示应用程序列表,但是抽出缓慢的滑动可以控制应用程序抽屉的运动。

我的问题如下:

  • 使小部件从屏幕外部滑入的正确方法是什么? Flutter抱怨我试图在屏幕外显示小部件,建议我使用ClipRect,但是我还没有找到ClipRect仅显示屏幕大小的方法(它似乎可以根据孩子的大小进行调整) )
  • 我要做什么建议的布局?当前,我在列中有问题和答案,并且为了最初使问题居中并隐藏问题,我修改了填充。感觉有点像黑客。
  • 是否有一个帮助程序库可以帮助我实现所要执行的确切滑动/拖动动作?它需要考虑动量和位置,以使运动感觉与android 8 app抽屉一样自然。

谢谢您的任何建议。

这是我到目前为止的屏幕:

问题屏幕

question screen

答案屏幕(向上滑动后)

answer screen

这是代码:

import 'package:flutter/material.dart';
import 'dart:math';
// Uncomment lines 7 and 10 to view the visual layout at runtime.
//import 'package:flutter/rendering.dart' show debugPaintSizeEnabled;

void main() {
  //debugPaintSizeEnabled = true;
  runApp(MyApp());
}

/*
 * travel around the world
 * 環遊世界
 * wàan jàu sâi gâai
 */

class Card extends StatefulWidget {
  @override
  createState() => CardState();
}

class CardState extends State<Card> with SingleTickerProviderStateMixin {
  var _dragStartOffset;

  Animation<double> questionAnimation;
  Animation<double> answerAnimation;
  Animation<double> opacityAnimation;
  AnimationController controller;

  initState() {
    super.initState();
    controller = AnimationController(duration: const Duration(milliseconds: 250), vsync: this);
    questionAnimation = Tween(begin: 250.0, end: 150.0).animate(controller)
      ..addListener(() {
        setState(() {
          // the state that has changed here is the animation object’s value
        });
      });
    answerAnimation = Tween(begin: 200.0, end: 32.0).animate(controller)
      ..addListener(() {
        setState(() {
          // the state that has changed here is the animation object’s value
        });
      });
    opacityAnimation = Tween(begin: 0.0, end: 1.0).animate(controller)
      ..addListener(() {
        setState(() {
          // the state that has changed here is the animation object’s value
        });
      });

  }


  @override
  Widget build(BuildContext context) {
    Widget question = Container(
        padding:  EdgeInsets.only(top: questionAnimation.value),
        child: Center (
            child: Text(
              "travel around the world",
              style: TextStyle(
                fontWeight: FontWeight.bold,
                fontSize: 48.0,
              ),
              textAlign: TextAlign.center,
            )
        ),
    );

    Widget answer = Container(
        padding:  EdgeInsets.only(top: answerAnimation.value),
        child: Opacity(
          opacity: opacityAnimation.value,
          child: Text(
              "wàan jàu sâi gâai 環遊世界",
              style: TextStyle(
                fontWeight: FontWeight.bold,
                fontSize: 48.0,
              ),
              textAlign: TextAlign.center,
            )
        )
    );

    var children = [question, answer];

    var child = GestureDetector(
        onTap: () {
            controller.reset();
        },
        onVerticalDragUpdate: (data) {
          // print(data);
          var currentOffset = data.globalPosition;
          var travel = _dragStartOffset - currentOffset;
          // print(travel);

          if(travel.dy <0 )
          {
            return;
          }

          // cannot be lower than zero
          var travelY = max<double>(0.0, travel.dy);
          // cannot be higher than 100
          travelY = min<double>(200.0, travelY);

          var animationPosition = travelY / 200.0;
          controller.value = animationPosition;
        },
        onVerticalDragEnd: (data) {
          if(controller.value > 0.50) {
            // make the animation continue on its own
            controller.forward();
          } else {
            // go back the other way
            controller.reverse();
          }
        },
        onVerticalDragStart: (data) {
          //print(data);
          _dragStartOffset = data.globalPosition;
        },
        child: Scaffold(
          appBar: AppBar(
            title: Text('AnkiReview'),
          ),
          body:  Container(
              child:Column(
                children: children,
              )
          ),
        )

    );


    return child;



  }
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {

    return MaterialApp(
      title: 'Flutter Demo',
      home: Card(),
    );
  }
}

1 个答案:

答案 0 :(得分:0)

我想出了一个解决方案。它涉及一个,顶部只是一个带有问题的容器,而底部是一个具有空白首页的 PageView 。用户可以向上滑动以显示答案。

它解决了裁剪问题,也解决了物理问题,因为PageView具有内置的物理和捕捉功能,否则将很难构建(我可能必须使用CustomScrollView)。

代码:

// Copyright 2017 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';
import 'dart:math';
// Uncomment lines 7 and 10 to view the visual layout at runtime.
//import 'package:flutter/rendering.dart' show debugPaintSizeEnabled;

void main() {
  //debugPaintSizeEnabled = true;
  runApp(MyApp());
}

/*
 * travel around the world
 * 環遊世界
 * wàan jàu sâi gâai
 */

class Card extends StatefulWidget {
  @override
  createState() => CardState();
}

class CardState extends State<Card> with SingleTickerProviderStateMixin {
  var _dragStartOffset;

  var _fontSize = 48.0;
  static const _padding = 28.0;

  initState() {
    super.initState();
  }


  @override
  Widget build(BuildContext context) {

    var questionText = Text(
      "travel around the world",
      style: TextStyle(
        fontWeight: FontWeight.bold,
        fontSize: _fontSize,
      ),
      textAlign: TextAlign.center,
    );

    var answerText = Text(
        "wàan jàu sâi gâai 環遊世界",
        style: TextStyle(
          fontWeight: FontWeight.bold,
          fontSize: _fontSize,
        ),
        textAlign: TextAlign.center
    );


    Widget question = Container(
            padding: EdgeInsets.only(bottom: _padding),
            alignment: Alignment.bottomCenter,
            child: questionText
    );

    Widget answer = Container(
        padding:  EdgeInsets.only(top: _padding),
        alignment: Alignment.topCenter,
        child: answerText

    );

    var card = Column(
      children: [
        Expanded(
          child: question,
        ),
        Expanded(
          child: PageView(
          scrollDirection: Axis.vertical,
            children: [
              Container(),
              answer
            ]
          )
        )
      ]
    );

    return card;

  }
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {

    return MaterialApp(
      title: 'Flutter Demo',
      home: Scaffold(
        appBar: AppBar(
          title: Text('AnkiReview'),
        ),
        body:  Container(
            child:Card()
        ),
      ),
    );
  }
}