联系我们
简单又实用的WordPress网站制作教学
当前位置:网站首页 > 程序开发学习 > 正文

Flutter自定义View之任务回传控制

作者:访客发布时间:2023-12-07分类:程序开发学习浏览:93


导读:简介Flutter作为UI框架,和其他语言一样支持自定义View。Inthispaper,wewillintroducehowtouseFluttertoach...

简介

Flutter作为UI框架,和其他语言一样支持自定义View。In this paper,we will introduce how to use Flutter to achieve a randomized suspension control.片尾附源码。

场景

作为一个程序员,听歌几乎是写代码的必备。一般的听力软件都会有这样一个效果,当我们听到时,打开另一个桌面时,正在播放歌曲的桌面以一个CD盘的形式悬浮在桌面上,可以随意点击CD盘又会重新回到当前播放的桌面上。

先看效果:

实现功能

This article mainly discusses how to use Flutter to achieve this effect.

需要实现的功能如下:

  1. 支持自定义初始位置。
  2. 随意拖动悬浮控件。
  3. 控件支持回弹效果。
  4. 控件不能滑出页面。

功能分析

初始位置

在Flutter定义控件的位置一般使用Positioned控件。通过指定left和top属性来初始化浮动按钮的位置。

  1. The most important part of this study is to explore the relationship between the mortality and the incidence of hyperlipidemia in patients with hyperlipidemia.
late Offset _offset;
  1. Then将初始值的值发送给Positioned Control.
Positioned(
  left: _offset.dx,
  top: _offset.dy,
  ....
)

这样就实现了初始化控件的位置。

随意拖拽

要想实现手势功能,在Flutter中就不得不使用GestureDetector 了,GestureDetector提供大量的手势操作回调,简化用户的使用。这里主要使用GestureDetector

onTap:点击,

onPanStart:滑动启动,

onPanUpdate:滑动更新,

onPanEnd:滑动结束

这几个方法。Through calculating the sliding position of the finger on the screen,then updating the control UI can be finished.

GestureDetector(
  //滑动开始
  onPanStart: (DragStartDetails details) {
    _animationController.stop(canceled: true);
    _selfWidth = context.size?.width ??0;
    _selfHeight =context.size?.height ??0;
  },
  //滑动更新
  onPanUpdate: (DragUpdateDetails details) {
    _offset += Offset(details.delta.dx, details.delta.dy);
    _offsetTop += details.delta.dy;
    _offsetLeft += details.delta.dx;
    if (_offsetTop < 0) _offsetTop = 0 + (widget.marginTop ?? 0.0);
    if (_offsetLeft < 0) _offsetLeft = 0 + (widget.marginLeft ?? 0.0);
    if (_offsetLeft + _selfWidth > _screenWidth) {
      _offsetLeft = _screenWidth - _selfWidth;
    }
    if (_offsetTop + _selfHeight > _screenHeight) {
      _offsetTop = _screenHeight - _selfHeight;
    }
    //重绘UI
    setState(() {});
  },
  //滑动结束
  onPanEnd: (DragEndDetails details) {
    if (_offsetLeft > _screenWidth / 2 - _selfWidth / 2) {
      _offsetLeft =
          _screenWidth - _selfWidth - (widget.marginRight ?? 0.0);
    } else {
      _offsetLeft = 0.00 + (widget.marginLeft ?? 0.0);
    }

回弹效果

处理滑翔动作,要增加回弹效果,就是要使用Flutter提供的动画。这里使用的是弹簧效果。

  1. 定义弹簧动画效果。
final double? mass; //弹簧质量
  final double? stiffness; //弹簧系数
  final double? damping; //阻尼系数

_animation = _animationController.drive(
    Tween<Offset>(begin: _offset, end: Offset(_offsetLeft, _offsetTop)));
SpringSimulation simulation = SpringSimulation(
  SpringDescription(mass: _mass, stiffness: _stiffness, damping: _damping),
  0,
  1,
  -Offset(pixelsPerSecond.dx / size.width, pixelsPerSecond.dy / size.height)
      .distance,
);
_animationController.animateWith(simulation);

可通过调节弹簧质量,弹簧系数,阻尼系数来控制得到不同的弹簧效果。

  1. In this paper,the author introduces the principle and application of the mechanism of mechanical mechanism.
_animationController = AnimationController.unbounded(vsync: this);
_animationController.addListener(() {
  _offset = _animation.value;
  setState(() {});
});

边界处理

最后我们来处理一下边界问题,如果不小心将控件滑出屏幕 ,那岂不是使用不了。所以当将控件滑出屏幕时,控件能够自动滑到屏幕两边。这里我们需要再手势滑动过程中,判断是否超出了屏幕边界。

 onPanUpdate: (DragUpdateDetails details) {
          _offset += Offset(details.delta.dx, details.delta.dy);
          _offsetTop += details.delta.dy;
          _offsetLeft += details.delta.dx;
          if (_offsetTop < 0) _offsetTop = 0 + (widget.marginTop ?? 0.0);
          if (_offsetLeft < 0) _offsetLeft = 0 + (widget.marginLeft ?? 0.0);
          if (_offsetLeft + _selfWidth > _screenWidth) {
            _offsetLeft = _screenWidth - _selfWidth;
          }
          if (_offsetTop + _selfHeight > _screenHeight) {
            _offsetTop = _screenHeight - _selfHeight;
          }
          setState(() {});
        },

通过实时监听手指在屏幕的偏移位置来限制控件的位置。

难点

计算屏幕高度:

  WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
      final RenderBox parentRenderBox =
      widget.parentKey.currentContext?.findRenderObject() as RenderBox;
      _screenHeight = parentRenderBox.size.height - (widget.marginBottom ?? 0.0);
    });

The height of the cylindrical portion 110 is determined based on the height of the cylindrical portion 110 and the cylindrical portion 112.

源码如下:

import 'package:flutter/material.dart';
import 'package:flutter/physics.dart';

class OverlayButton extends StatefulWidget {
  const OverlayButton(
      {Key? key,
      required this.child,
      required this.parentKey,
      required this.initOffset,
      required this.onPressed,
      this.marginLeft,
      this.marginTop,
      this.marginBottom,
      this.marginRight,
      this.mass,
      this.stiffness,
      this.damping})
      : super(key: key);
  final Widget child; //子widget
  final Offset initOffset; //初始位置
  final GlobalKey parentKey; //父控件的key
  final VoidCallback onPressed; //点击事件
  final double? marginLeft; //外边距,距离左边
  final double? marginTop; //外边距,距离上边
  final double? marginRight; //外边距,距离右边
  final double? marginBottom; //外边距,距离下边
  final double? mass; //弹簧质量
  final double? stiffness; //弹簧系数
  final double? damping; //阻尼系数

  @override
  State createState() => _OverlayButtonState();
}

class _OverlayButtonState extends State<OverlayButton>
    with SingleTickerProviderStateMixin {
  late double _offsetLeft;
  late double _offsetTop;
  late AnimationController _animationController;
  late Animation<Offset> _animation;
  late Offset _offset;
  late double _screenWidth;
  late double _screenHeight;
  late double _selfWidth = 0.0;
  late double _selfHeight = 0.0;
  late double _mass;

  late double _stiffness;
  late double _damping;

  @override
  void initState() {
    super.initState();
    _mass = widget.mass ?? 20;
    _stiffness = widget.stiffness ?? 400;
    _damping = widget.damping ?? 0.75;
    _offsetLeft = widget.initOffset.dx;
    _offsetTop = widget.initOffset.dy;
    _offset = widget.initOffset;
    _animationController = AnimationController.unbounded(vsync: this);
    _animationController.addListener(() {
      _offset = _animation.value;
      setState(() {});
    });
    WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
      final RenderBox parentRenderBox =
      widget.parentKey.currentContext?.findRenderObject() as RenderBox;
      _screenHeight = parentRenderBox.size.height - (widget.marginBottom ?? 0.0);
    });
  }

  @override
  Widget build(BuildContext context) {
    Size size = MediaQuery.of(context).size;
    _screenWidth = size.width;

    return Positioned(
      left: _offset.dx,
      top: _offset.dy,
      child: GestureDetector(
        onPanStart: (DragStartDetails details) {
          _animationController.stop(canceled: true);
          _selfWidth = context.size?.width ??0;
          _selfHeight =context.size?.height ??0;
        },
        onPanUpdate: (DragUpdateDetails details) {
          _offset += Offset(details.delta.dx, details.delta.dy);
          _offsetTop += details.delta.dy;
          _offsetLeft += details.delta.dx;
          if (_offsetTop < 0) _offsetTop = 0 + (widget.marginTop ?? 0.0);
          if (_offsetLeft < 0) _offsetLeft = 0 + (widget.marginLeft ?? 0.0);
          if (_offsetLeft + _selfWidth > _screenWidth) {
            _offsetLeft = _screenWidth - _selfWidth;
          }
          if (_offsetTop + _selfHeight > _screenHeight) {
            _offsetTop = _screenHeight - _selfHeight;
          }
          setState(() {});
        },
        onPanEnd: (DragEndDetails details) {
          if (_offsetLeft > _screenWidth / 2 - _selfWidth / 2) {
            _offsetLeft =
                _screenWidth - _selfWidth - (widget.marginRight ?? 0.0);
          } else {
            _offsetLeft = 0.00 + (widget.marginLeft ?? 0.0);
          }

          startAnimation(details.velocity.pixelsPerSecond, size);
        },
        onTap: widget.onPressed,
        child: widget.child,
      ),
    );
  }

  void startAnimation(Offset pixelsPerSecond, Size size) {
    _animation = _animationController.drive(
        Tween<Offset>(begin: _offset, end: Offset(_offsetLeft, _offsetTop)));
    SpringSimulation simulation = SpringSimulation(
      SpringDescription(mass: _mass, stiffness: _stiffness, damping: _damping),
      0,
      1,
      -Offset(pixelsPerSecond.dx / size.width, pixelsPerSecond.dy / size.height)
          .distance,
    );
    _animationController.animateWith(simulation);
  }
  @override
  void dispose() {
    super.dispose();
    _animationController.dispose();
  }
}

本文简单实现了一个拖拽回弹的控件,更多好玩的效果需要动手去实现。


标签:自定义控件拖拽颤振视图


程序开发学习排行
最近发表
网站分类
标签列表