Flutter自定义Appbar搜索框

5,528 阅读2分钟

首先看一下实现本次实现的效果。 在这里插入图片描述 在这里插入图片描述 本来想直接偷懒从flutter pub上找个能用的使用起来,但是看了下发现都与目前ui效果相差很大,于是乎决定自己实现一个。整体的话比较简单,本来也是为了练手而做的。

为了方便处理statusbar的高度适配,于是乎直接依托于Appbar进行了实现,这样就可以不用处理状态栏适配了。

class _HotWidgetState extends State<HotWidget> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        titleSpacing: 0,  //清除title左右padding,默认情况下会有一定的padding距离
        toolbarHeight: 44, //将高度定到44,设计稿的高度。为了方便适配,
        //推荐使用插件flutter_screenutil做屏幕的适配工作
        backgroundColor: Colors.white,
        elevation: 0,
        //由于title本身是接受一个widget,所以可以直接给他一个自定义的widget。
        title: SearchAppBar(
          hintLabel: "电影/电视剧/影人",
        ),
      ),
      body: Container(),
    );
  }
}

flutter中控件定义推荐的是使用组合控件实现,这个是真的很酷,因为万物皆widget,组合起来很方便。

import 'package:flutter/material.dart';
import 'package:flutter_demo_001/ui.theme/color.dart';
import 'package:flutter_demo_001/ui.theme/theme.dart';
import 'package:flutter_demo_001/utils/padding.dart';

class SearchAppBar extends StatefulWidget {
  SearchAppBar({Key? key, required this.hintLabel}) : super(key: key);

  final String hintLabel;

  @override
  State<StatefulWidget> createState() {
    return SearchAppBarState();
  }
}

class SearchAppBarState extends State<SearchAppBar> {
  late FocusNode _focusNode;

  ///默认不展示控件

  bool _offstage = true;

  ///监听TextField内容变化
  final TextEditingController _textEditingController = TextEditingController();

  @override
  void initState() {
    super.initState();
    _focusNode = FocusNode();
    _textEditingController.addListener(() {
      var isVisible = _textEditingController.text.isNotEmpty;
      _updateDelIconVisible(isVisible);
    });
  }

  _updateDelIconVisible(bool isVisible) {
    setState(() {
      _offstage = !isVisible;
    });
  }

  @override
  Widget build(BuildContext context) {
    return SizedBox(
      width: double.infinity,
      height: 30,
      child: Row(
        children: [
          Expanded(
            flex: 1,
            child: Container(
              height: double.infinity,
              margin: const EdgeInsets.only(left: 16),
              decoration: BoxDecoration(
                  color: colorF5F6FA, borderRadius: BorderRadius.circular(4)),
              child: Row(
                crossAxisAlignment: CrossAxisAlignment.center,
                children: [
                  paddingOnly(const EdgeInsets.only(left: 8)),
                  Image.asset(
                    "images/home_search.png",
                    width: 16,
                    height: 16,
                  ),
                  paddingOnly(const EdgeInsets.only(left: 8)),
                  Expanded(
                    flex: 1,
                    child: TextField(
                      controller: _textEditingController,
                      autofocus: true,
                      focusNode: _focusNode,
                      style: TextStyle(fontSize: 14, color: color333),
                      decoration: boxInputDecoration(widget.hintLabel),
                      maxLines: 1,
                    ),
                  ),
                  paddingOnly(const EdgeInsets.only(right: 8)),
                  Offstage(
                    offstage: _offstage,
                    child: GestureDetector(
                      onTap: () => {_textEditingController.clear()},
                      child: Image.asset(
                        "images/home_search_cancel.png",
                        width: 16,
                        height: 16,
                      ),
                    ),
                  ),
                  paddingOnly(const EdgeInsets.only(right: 8)),
                ],
              ),
            ),
          ),
          GestureDetector(
            onTap: () {
              _focusNode.unfocus();
            },
            child: Container(
              padding: const EdgeInsets.only(left: 16, right: 16),
              child: Text("取消",
                  style: TextStyle(fontSize: 16, color: Color(0xFF3D7DFF))),
            ),
          ),
        ],
      ),
    );
  }

  @override
  void dispose() {
    super.dispose();
    _focusNode.unfocus();
  }
}

最后为了美观,需要让状态栏也变成透明。

void main() {
  //固定屏幕旋转方向
  WidgetsFlutterBinding.ensureInitialized();
  SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp])
      .then((_) {
    runApp(const MyApp());
  });
  //设置状态栏透明
  if (Platform.isAndroid) {
    SystemUiOverlayStyle systemUiOverlayStyle = const SystemUiOverlayStyle(
      statusBarColor: Colors.transparent, //设置为透明
    );
    SystemChrome.setSystemUIOverlayStyle(systemUiOverlayStyle);
  }
}