将 OverlayEntry Widget 推到键盘上方

Md.*_*min 7 keyboard overlay flutter

我创建了一个自定义小部件,其中覆盖条目小部件显示在其右下方,并带有搜索栏和列表。

我面临的问题是,当用户单击覆盖小部件内的文本字段时,屏幕没有向上推得足以让用户看到文本字段。请检查屏幕截图以了解更多详细信息

自定义下拉按钮小部件的代码如下:

import 'package:flutter/material.dart';

class DropDownWidget2 extends StatefulWidget {
  DropDownWidget2({Key? key}) : super(key: key);

  @override
  State<DropDownWidget2> createState() => _DropDownWidget2State();
}

class _DropDownWidget2State extends State<DropDownWidget2> with TickerProviderStateMixin {
  late AnimationController _animationController;
  late Animation<double> _animation;
  bool isOpen = false;

  final List<Map<String, dynamic>> _allData = [
    {"id": 1, "name": "Andy", "age": 29},
    {"id": 2, "name": "Aragon", "age": 40},
    {"id": 3, "name": "Bob", "age": 5},
    {"id": 4, "name": "Barbara", "age": 35},
    {"id": 5, "name": "Candy", "age": 21},
    {"id": 6, "name": "Colin", "age": 55},
    {"id": 7, "name": "Audra", "age": 30},
    {"id": 8, "name": "Banana", "age": 14},
    {"id": 9, "name": "Caversky", "age": 100},
    {"id": 10, "name": "Becky", "age": 32},
  ];

  // This list holds the data for the list view
  List<Map<String, dynamic>> _foundData = [];

  final layerLink = LayerLink();
  OverlayEntry? entry;

  @override
  void initState() {
    _animationController = AnimationController(vsync: this, duration: Duration(milliseconds: 450));
    _animation = Tween(begin: 0.0, end: -.5)
        .animate(CurvedAnimation(parent: _animationController, curve: Curves.easeOut));
    _foundData = _allData;

    super.initState();
  }

  @override
  void dispose() {
    _animationController.dispose();
    super.dispose();
  }

  void _handleOnPressed() {
    setState(() {
      isOpen = !isOpen;
      isOpen ? _animationController.forward() : _animationController.reverse();
      showOverLay();
    });
  }

  @override
  Widget build(BuildContext context) {
    return CompositedTransformTarget(
      link: layerLink,
      child: Container(
        padding: EdgeInsets.symmetric(horizontal: 10),
        decoration: BoxDecoration(
            border: Border.all(color: Colors.black), borderRadius: BorderRadius.circular(10)),
        child: Row(
          mainAxisSize: MainAxisSize.max,
          mainAxisAlignment: MainAxisAlignment.spaceBetween,
          children: [
            Text('Select one from below'),
            IconButton(
              padding: EdgeInsets.zero,
              iconSize: 18,
              splashRadius: 20,
              splashColor: Colors.greenAccent,
              icon: RotationTransition(
                turns: _animation,
                child: Icon(Icons.expand_more),
              ),
              onPressed: () => _handleOnPressed(),
            )
          ],
        ),
      ),
    );
  }

  Widget itemsWidget() {
    return Container(
      width: MediaQuery.of(context).size.width,
      decoration: BoxDecoration(borderRadius: BorderRadius.circular(10)),
      padding: EdgeInsets.only(left: 12, top: 4, bottom: 4),
      child: Column(
        mainAxisSize: MainAxisSize.min,
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          TextField(
            onChanged: (value) => {},
            decoration: const InputDecoration(labelText: 'Search', suffixIcon: Icon(Icons.search)),
          ),
          _foundData.isNotEmpty
              ? ListView.separated(
                  padding: EdgeInsets.zero,
                  shrinkWrap: true,
                  itemCount: _foundData.length,
                  itemBuilder: (context, index) {
                    return Text(_foundData[index]['name']);
                  },
                  separatorBuilder: (context, index) {
                    return Divider();
                  },
                )
              : const Text(
                  'No results found',
                  style: TextStyle(fontSize: 14),
                ),
        ],
      ),
    );
  }

  Widget buildOverlay() {
    return Material(
      shape: RoundedRectangleBorder(
        borderRadius: BorderRadius.circular(10),
        side: const BorderSide(color: Colors.black, width: 1),
      ),
      child: AnimatedCrossFade(
        duration: const Duration(microseconds: 500),
        firstChild: Container(), // When you don't want to show menu..
        secondChild: itemsWidget(),
        crossFadeState: isOpen ? CrossFadeState.showSecond : CrossFadeState.showFirst,
      ),
    );
  }

  void showOverLay() {
    final overlay = Overlay.of(context)!;
    final renderBox = context.findRenderObject() as RenderBox;
    final size = renderBox.size;

    entry = OverlayEntry(
        builder: (context) => Positioned(
            width: size.width,
            child: CompositedTransformFollower(
                link: layerLink,
                showWhenUnlinked: false,
                offset: Offset(0, size.height),
                child: buildOverlay())));

    overlay.insert(entry!);
  }
}
Run Code Online (Sandbox Code Playgroud)

正如您可以从屏幕截图中观察到的那样。键盘移动到覆盖条目上方,隐藏我的文本字段和列表项。有什么办法可以将整个overlayEntry推到键盘上方吗?我已经尝试了很多方法,例如使用 mediaquery 底部插图和其他东西,但我仍然无法实现它。

不带键盘带键盘

DTo*_*odt 0

也许您可以尝试以下其中一项:

  • 向页面小部件添加填充;
  • 向文本字段添加填充;

首先,您可以添加包裹主体的填充:

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: MediaQuery.of(context).viewInsets,
      child: The page ...,
    );
  }
Run Code Online (Sandbox Code Playgroud)

使用第二个选项,您可以添加等于键盘大小的底部填充。在页面(StatefulWidget),您可以添加didChangeDependencies

  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    setState(() {
      _keyboardSize = MediaQuery.of(context).viewInsets.bottom;
    });
  }
Run Code Online (Sandbox Code Playgroud)

在您的 TextField 中,您设置scrollPadding属性:

  scrollPadding: EdgeInsets.only(
    bottom: _keyboardSize,
  ),
Run Code Online (Sandbox Code Playgroud)