在 flutter AppBar 中的自定义搜索栏内设置动画

Animate inside a custom search bar in flutter AppBar

我已经实现了一个自定义搜索栏,可以在用户选择它时进行切换。我正在替换我需要的 appBar 中的 SearchBar。 最初,搜索栏将显示一个带有 String("Search")==> view1 的 Icon(search),当单击它们中的任何一个时,它将被另一个具有可搜索文本字段的视图替换 ==> view 2。 我会 post 我到目前为止所做的。 我需要给它一个漂亮的动画。 动画应该像这样移动。 最初显示 view1 然后 ==> 动画 view2 从右到左替换 view1 当需要再次返回 view1 时 ==> 反转上面的动画。

目前的解决方法

class SearchBar extends StatefulWidget with PreferredSizeWidget {
  const SearchBar({Key? key}) : super(key: key);

  @override
  _SearchBarState createState() => _SearchBarState();

  @override
  Size get preferredSize => Size.fromHeight(kToolbarHeight);
}

class _SearchBarState extends State<SearchBar> {
  bool _toggle = true;

  @override
  Widget build(BuildContext context) {
    return AppBar(
      elevation: 0.0,
      backgroundColor: CustomColors.mWhite,
      automaticallyImplyLeading: false,
      title:
          AnimatedContainer(
        width: MediaQuery.of(context).size.width * 0.8,
        decoration: _toggle
            ? null
            : BoxDecoration(
                color: Colors.white,
                borderRadius: BorderRadius.circular(15.0),
                border: Border.all(
                  width: 1,
                  color: CustomColors.grey600,
                ),
              ),
        duration: Duration(seconds: 2),
        child: _toggle
            ? GestureDetector(
                onTap: () {
                  setState(() {
                    _toggle = !_toggle;
                  });
                },
                child: Row(
                  mainAxisAlignment: MainAxisAlignment.start,
                  crossAxisAlignment: CrossAxisAlignment.center,
                  children: [
                    Icon(
                      Icons.search,
                      size: 24.0,
                    ),
                    SizedBox(
                      width: 10.0,
                    ),
                    Text(
                      "Search",
                      style: tBody1,
                    ),
                  ],
                ),
              )
            : Center(
                child: TextField(
                  textInputAction: TextInputAction.search,
                  decoration: InputDecoration(
                      prefixIcon: IconButton(
                        icon: Icon(Icons.arrow_back_ios),
                        onPressed: () {
                          setState(() {
                            _toggle = !_toggle;
                          });
                        },
                      ),
                      border: InputBorder.none),
                ),
              ),
      ),
      actions: [
        Container(
          width: 50.0,
          height: 50.0,
          padding: EdgeInsets.only(right: 8.0),
          child: Image.asset(
            'assets/images/settings.png',
          ),
        ),
      ],
    );
  }
}

AnimatedContainer 应该有一个 width 取决于 _toggle 值,在您的情况下,只有搜索字段应该包含在 AnimatedContainer 中。

我稍微简化了您的代码以展示如何使动画正常工作,请参见下文。 transform参数负责从右到左动画,如果你注释掉它,它会从左到右动画。

我还添加了一个 AnimatedOpacity 来淡入/淡出后退图标。

(您可以将代码原样粘贴到 DartPad 中以查看其工作原理。)

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return const MaterialApp(home: Scaffold(appBar: SearchBar()));
  }
}

class SearchBar extends StatefulWidget with PreferredSizeWidget {
  const SearchBar({Key? key}) : super(key: key);

  @override
  _SearchBarState createState() => _SearchBarState();

  @override
  Size get preferredSize => const Size.fromHeight(kToolbarHeight);
}

class _SearchBarState extends State<SearchBar> {
  bool _toggle = true;

  void _doToggle() => setState(() => _toggle = !_toggle);

  @override
  Widget build(BuildContext context) {
    return AppBar(
      title: Stack(children: [
        GestureDetector(
          onTap: _doToggle,
          child: SizedBox(
              height: kToolbarHeight * 0.8,
              child: Row(
                children: const [
                  Icon(
                    Icons.search,
                    size: 24.0,
                  ),
                  SizedBox(
                    width: 10.0,
                  ),
                  Text("Search"),
                ],
              )),
        ),
        AnimatedContainer(
          width: _toggle ? 0 : MediaQuery.of(context).size.width,
          transform: Matrix4.translationValues(_toggle ? MediaQuery.of(context).size.width : 0, 0, 0),
          duration: const Duration(seconds: 1),
          height: kToolbarHeight * 0.8,
          decoration: BoxDecoration(
            color: Colors.white,
            borderRadius: BorderRadius.circular(15.0),
            border: Border.all(
              width: 1,
              color: Colors.grey[600]!,
            ),
          ),
          child: TextField(
            textInputAction: TextInputAction.search,
            decoration: InputDecoration(
                prefixIcon: AnimatedOpacity(
                    duration: const Duration(seconds: 1),
                    opacity: _toggle ? 0 : 1,
                    child: IconButton(
                      icon: const Icon(Icons.arrow_back_ios),
                      onPressed: _doToggle,
                    )),
                border: InputBorder.none),
          ),
        )
      ]),
    );
  }
}