将Flutter TextField平滑地设置为文本动画

时间:2019-10-13 22:26:27

标签: flutter flutter-animation

我希望能够将TextField转换为仅与Text之间的过渡,这意味着输入修饰逐渐消失,并且文本本身稍微向左移动(当输入填充变为0时)。

使用TextField的固有动画,以下代码取得了一些有限的成功。但是,文本会向左跳,而不是逐渐向左移动,并且输入的修饰会跳入/跳出以及淡入淡出。

class ExampleScreen extends StatefulWidget {
  @override
  ExampleScreenState createState() => ExampleScreenState();
}

class ExampleScreenState extends State<ExampleScreen> {
  TextEditingController text = TextEditingController();
  bool editing = true;
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: EditableText(controller: text, editing: editing),
      floatingActionButton: FloatingActionButton(
        onPressed: () => setState(() => editing = !editing),
        child: Icon(Icons.ac_unit),
      ),
    );
  }
}

class EditableText extends StatelessWidget {
  final TextEditingController controller;
  final bool editing;
  EditableText({this.controller, this.editing});

  @override Widget build(BuildContext context) {
    return TextField(
      controller: controller,
      enabled: editing,
      decoration: InputDecoration().copyWith(
          contentPadding: editing ? Theme.of(context).inputDecorationTheme.contentPadding : EdgeInsets.zero,
          disabledBorder: OutlineInputBorder(borderSide: BorderSide(color: Colors.transparent))
      ),
    );
  }
}

有什么聪明的主意吗?

2 个答案:

答案 0 :(得分:1)

我写了一个有效的示例,但是仍然有些闪烁,您可以尝试一些[曲线]来修改动画。

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: HomePage(),
    );
  }
}

class HomePage extends StatefulWidget {
  @override
  _HomePageState createState() => _HomePageState();
}

class _HomePageState extends State<HomePage>
    with SingleTickerProviderStateMixin {
  AnimationController controller;

  @override
  void initState() {
    super.initState();
    controller = AnimationController(
      duration: Duration(seconds: 1),
      vsync: this,
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Home Page'),
      ),
      body: Center(
        child: AnimatedTextField(
          controller: controller,
        ),
      ),
      floatingActionButton: FloatingActionButton(
        child: Icon(Icons.opacity),
        onPressed: () {
          var status = controller.status;
          if (status == AnimationStatus.completed) {
            controller.reverse();
          } else if (status == AnimationStatus.dismissed) {
            controller.forward();
          }
        },
      ),
    );
  }
}

class AnimatedTextField extends StatefulWidget {
  final AnimationController controller;
  final Animation<double> opacity;
  final Animation<double> left;

  AnimatedTextField({
    Key key,
    @required this.controller,
  })  : opacity = Tween<double>(
          begin: 1.0,
          end: 0.0,
        ).animate(
          CurvedAnimation(
            parent: controller,
            curve: Interval(
              0.0,
              0.5,
              curve: Curves.easeInOut,
            ),
          ),
        ),
        left = Tween<double>(
          begin: 20.0,
          end: 0.0,
        ).animate(
          CurvedAnimation(
            parent: controller,
            curve: Interval(
              0.0,
              1.0,
              curve: Curves.easeIn,
            ),
          ),
        ),
        super(key: key);

  @override
  _AnimatedTextFieldState createState() => _AnimatedTextFieldState();
}

class _AnimatedTextFieldState extends State<AnimatedTextField> {
  TextEditingController _textEditingController =
      TextEditingController(text: 'hello fluttr');
  @override
  Widget build(BuildContext context) {
    return AnimatedBuilder(
      animation: widget.controller,
      builder: (context, child) {
        var theme = Theme.of(context);
        var o = widget.opacity.value;
        var _child = o > 0.0
            ? TextField(
                controller: _textEditingController,
                style: theme.textTheme.body2,
                decoration: InputDecoration(
                  focusedBorder: UnderlineInputBorder(
                    borderSide: BorderSide(color: Color.fromRGBO(0, 0, 0, 1)),
                  ),
                  enabledBorder: UnderlineInputBorder(
                    borderSide: BorderSide(color: Color.fromRGBO(0, 0, 0, o)),
                  ),
                ),
              )
            : Opacity(
                opacity: (o - 1).abs(),
                child: Align(
                  alignment: Alignment.centerLeft,
                  child: Text(
                    _textEditingController.text,
                    style: theme.textTheme.body2,
                  ),
                ),
              );
        return Padding(
          padding: EdgeInsets.only(
            left: widget.left.value,
            top: 20,
            right: 20,
            bottom: 20,
          ),
          child: _child,
        );
      },
    );
  }
}

答案 1 :(得分:0)

设法解决。创建了一个'AnimatedDouble'窗口小部件,该窗口小部件仅使一个动画值作为参数的构建器,并由ChangeNotifier触发:

class AnimatedDouble extends StatefulWidget {
  final Widget child;
  final Function(BuildContext context, Widget child, double d) builder;
  final Duration duration;
  final ChangeNotifier listen;
  AnimatedDouble({this.child, this.builder, this.duration, this.listen});
  @override
  _AnimatedDoubleState createState() => new _AnimatedDoubleState();
}

class _AnimatedDoubleState extends State<AnimatedDouble> with SingleTickerProviderStateMixin{
  AnimationController controller;
  @override
  void initState() {
    super.initState();
    controller = AnimationController(value: 1.0, vsync: this, duration: widget.duration);
    widget.listen.addListener(_go);
  }

  @override
  void dispose() { 
    controller.dispose();
    widget.listen.removeListener(_go);
    super.dispose();
  }

  void _go() {
    controller.forward(from: 0.0);
  }

  @override
  Widget build(BuildContext context) {
    return AnimatedBuilder(
      animation: controller,
      child: widget.child,
      builder: (context, child) => widget.builder(context, child, controller.view.value),
    );
  }
}

然后可以像这样使用它来获得我想要的效果:

AnimatedDouble(
  duration: Duration(milliseconds: 200),
  listen: something_to_trigger_animation,
  builder: (context, child, d) => TextField(
    controller: controller,
    decoration: InputDecoration(
      contentPadding: editing ?
        EdgeInsets.symmetric(vertical: 16.0 * d, horizontal: 12.0 * d) :
        EdgeInsets.symmetric(vertical: 16.0 * (1 - d), horizontal: 12.0 * (1 - d)),
      disabledBorder: OutlineInputBorder(borderSide: BorderSide(color: Colors.transparent)),
    ),
    enabled: editing,
  ),
),