还有其他方法可以处理此覆盖,但据我所知,这应该可以工作,并且有时可以,但并不总是在 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);
}
花了很多时间才找到这个问题的原因。我不知道 MediaQuery 由于计时问题可以返回零的高度或宽度,并且应用程序必须满足这一点。看起来这工作正常,但高度和/或宽度设置为零。肯定有很多程序不检查这一点。