这个 Flutter Overlay 有什么问题

问题描述 投票:0回答:1

还有其他方法可以处理此覆盖,但据我所知,这应该可以工作,并且有时可以,但并不总是在 Android 上。在 Windows 上它似乎工作得很好。我有最新的 Flutter (3.0.3) 和最新的 Dart (2.17.5),Flutter doctor 显示没有问题。我已在 2 台物理设备(Android 6.0.1 - 级别 23 和 Android 11 - 级别 30)上进行了测试。我还在模拟器(Android 11 - 级别 30)上对其进行了测试。所有三个的结果非常相似 - 叠加并不总是显示。我还在 Windows 上对其进行了测试,所有尝试都完美运行 (30)。

下面的代码是演示该问题的代码的简化版本。问题是叠加层并不总是显示。重新启动后,它通常可以正常运行大约 10-12 次重新加载程序,然后就不一致,有时显示覆盖,有时不显示。正如我所说,它在 Windows 上完美运行。

import 'dart:io';
import 'package:flutter/services.dart';
import 'package:flutter/material.dart';

const D_OVERLAY_HEIGHT = 290.0;
const D_BTN_HEIGHT = 45.0;

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

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

class OverlayTest extends StatefulWidget {
  @override
  _PageOverlayTest createState() => _PageOverlayTest();
}

//==== THE MAIN PAGE FOR THE APP ====//
class _PageOverlayTest extends State<OverlayTest> {
  TextEditingController? _wController = TextEditingController();
  ClassOverlay? _clOverlay;
  @override
  Widget build(BuildContext context) {
    WidgetsFlutterBinding.ensureInitialized();
    WidgetsBinding.instance
        .addPostFrameCallback((_) => fnOnBuildComplete(context));
    return Scaffold(
        resizeToAvoidBottomInset: false,
        appBar: AppBar(
            leading: IconButton(
                icon: Icon(Icons.arrow_back), onPressed: () => fnExit()),
            title: Center(child: Text('Overlay Test'))),
        body: WillPopScope(
          onWillPop: () async => await _fnDispose(),
          child: Column(
            children: [
              SizedBox(height: 50),
              TextField(
                controller: _wController,
                decoration: InputDecoration(
                  border: OutlineInputBorder(
                    borderRadius: BorderRadius.circular(3),
                  ),
                ),
                style: TextStyle(fontSize: 16),
                autofocus: true,
                showCursor: true,
                readOnly: true,
                toolbarOptions: ToolbarOptions(
                  copy: false,
                  cut: false,
                  paste: false,
                  selectAll: false,
                ),
              ),
            ],
          ),
        ));
  }

  fnExit() {
    dispose();
    SystemNavigator.pop();
    exit(0);
  }

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

  Future<bool> _fnDispose() async {
    if (_wController != null) {
      _wController!.dispose();
      _wController = null;
    }
    if (_clOverlay != null) {
      _clOverlay!.fnDispose();
    }
    return true;
  }

  void fnOnBuildComplete(BuildContext context) async {
    if (_clOverlay == null) {
      double dScreenWidth = MediaQuery.of(context).size.width;
      dScreenWidth = dScreenWidth < 510 ? dScreenWidth : 500;
      double dScreenHeight = MediaQuery.of(context).size.height;
      _clOverlay = ClassOverlay(dScreenWidth, dScreenHeight - 320);
    }
    _clOverlay!.fnInitContext(context, _wController!);
  }
}

//==== CLASS FOR THE OVERLAY ====//
class ClassOverlay {
  TextEditingController? _wCtlText;
  OverlayEntry? _wOverlayEntry;
  final double dScreenWidth;
  final double dOverlayTop;
  Material? _wOverlayWidget;
  ClassOverlay(this.dScreenWidth, this.dOverlayTop) {
    SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual,
        overlays: [SystemUiOverlay.bottom, SystemUiOverlay.top]);
    _wOverlayWidget = fnCreateOverlayWidget(dScreenWidth: dScreenWidth);
  }

  //==== AFTER BUILD OF CALLER INITIALIZE WITH CONTEXT AND OVERLAYSTATE ====//
  void fnInitContext(BuildContext context, TextEditingController wCtlText) {
    try {
      _wCtlText = wCtlText;

      if (_wOverlayEntry != null) {
        _wOverlayEntry!.remove();
        _wOverlayEntry = null;
      }
      if (_wOverlayWidget == null) {
        throw ('_wOverlayWidget is null');
      }
      _wOverlayEntry = OverlayEntry(builder: (context) {
        return Positioned(left: 0, top: dOverlayTop, child: _wOverlayWidget!);
      });
      OverlayState? wOverlayState = Navigator.of(context).overlay;
      if (wOverlayState == null) {
        throw ('Failed to get OverlayState');
      }
      if (_wOverlayEntry == null) {
        throw ('OverlayEntry is null');
      }
      wOverlayState.insert(_wOverlayEntry!);
      if (!(wOverlayState.mounted)) {
        wOverlayState.activate();
      }
    } catch (vError) {
      _wCtlText!.text = '**** Fatal Error ${vError.toString()}';
    }
  }

  void fnDispose() {
    if (_wOverlayEntry != null) {
      _wOverlayEntry!.remove();
      _wOverlayEntry = null;
    }
  }
}

//==== CLASS - OVERLAY WIDGET ====//
Material fnCreateOverlayWidget({required double dScreenWidth}) {
  final double dBtnWidth = (dScreenWidth - 60) / 10;
  final double dLineSeparator = 10;
  return Material(
      child: Container(
    height: (D_OVERLAY_HEIGHT),
    width: dScreenWidth - 10,
    padding: EdgeInsets.only(bottom: 10),
    decoration: BoxDecoration(
        color: Colors.white,
        border: Border.all(color: Colors.grey.shade500, width: 2),
        borderRadius: BorderRadius.circular(10)),
    child: Column(
      children: [
        fnCreateRow('1234567890', dBtnWidth),
        SizedBox(height: dLineSeparator),
        fnCreateRow('qwertyuiop', dBtnWidth),
        SizedBox(height: dLineSeparator),
        fnCreateRow('asdfghjkl', dBtnWidth),
        SizedBox(height: dLineSeparator),
        fnCreateRow(',zxcvbnm.', dBtnWidth),
        SizedBox(height: dLineSeparator),
        fnCreateRow('1234567890', dBtnWidth)
      ],
    ),
  ));
}

//==== FUNCTION - CREATE A ROW OF TEXT BUTTONS ====//
Row fnCreateRow(String sRow, double dBtnWidth) {
  List<Widget> wRow = [];

  double dSpace = sRow.length == 10 ? 0 : (dBtnWidth * (10 - sRow.length)) / 2;
  if (dSpace > 0) {
    wRow.add(SizedBox(width: dSpace));
  }
  for (int iNdx = 0; iNdx < sRow.length; iNdx++) {
    double dFontSize = sRow[iNdx] == '.' || sRow[iNdx] == ',' ? 30 : 20;
    wRow.add(SizedBox(
        height: D_BTN_HEIGHT,
        width: dBtnWidth,
        child: TextButton(
          onPressed: () {},
          child: Text(sRow[iNdx],
              style: TextStyle(
                  color: Colors.black,
                  fontSize: dFontSize,
                  fontWeight: FontWeight.w400)),
        )));
  }
  if (dSpace > 0) {
    wRow.add(SizedBox(width: dSpace));
  }
  return Row(mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: wRow);
}

android flutter overlay
1个回答
0
投票

花了很多时间才找到这个问题的原因。我不知道 MediaQuery 由于计时问题可以返回零的高度或宽度,并且应用程序必须满足这一点。看起来这工作正常,但高度和/或宽度设置为零。肯定有很多程序不检查这一点。

© www.soinside.com 2019 - 2024. All rights reserved.