Skip to content

Commit

Permalink
migrate popup to null safety
Browse files Browse the repository at this point in the history
  • Loading branch information
hexintao committed Jan 12, 2022
1 parent 27dd619 commit 8427312
Show file tree
Hide file tree
Showing 3 changed files with 217 additions and 177 deletions.
23 changes: 13 additions & 10 deletions lib/src/components/popup/brn_measure_size.dart
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
// @dart=2.9

import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';

typedef void OnWidgetSizeChange(Size size);

/// 描述: 计算 Widget 宽高的工具类。
class MeasureSizeRenderObject extends RenderProxyBox {
Size oldSize;
Size? oldSize;
final OnWidgetSizeChange onChange;

MeasureSizeRenderObject(this.onChange);
Expand All @@ -16,23 +14,28 @@ class MeasureSizeRenderObject extends RenderProxyBox {
void performLayout() {
super.performLayout();

Size newSize = child.size;
Size newSize = Size.zero;
if (child != null) {
newSize = child!.size;
}
if (oldSize == newSize) return;

oldSize = newSize;
WidgetsBinding.instance.addPostFrameCallback((_) {
onChange(newSize);
});
if (WidgetsBinding.instance != null) {
WidgetsBinding.instance!.addPostFrameCallback((_) {
onChange(newSize);
});
}
}
}

class MeasureSize extends SingleChildRenderObjectWidget {
final OnWidgetSizeChange onChange;

const MeasureSize({
Key key,
@required this.onChange,
@required Widget child,
Key? key,
required this.onChange,
required Widget child,
}) : super(key: key, child: child);

@override
Expand Down
93 changes: 51 additions & 42 deletions lib/src/components/popup/brn_overlay_window.dart
Original file line number Diff line number Diff line change
@@ -1,12 +1,9 @@
// @dart=2.9

import 'dart:core';
import 'dart:math';
import 'dart:ui';

import 'package:bruno/src/components/popup/brn_measure_size.dart';
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';

/// popWindow位于targetView的方向
enum BrnOverlayPopDirection { none, top, bottom, left, right }
Expand All @@ -16,17 +13,17 @@ class BrnOverlayWindow extends StatefulWidget {
final BuildContext context;

/// 锚点 Widget 的 key,用于 OverlayWindow 的定位
final Key targetKey;
final GlobalKey targetKey;

/// OverlayWindow 相对于 key 的展示位置, 默认 bottom
final BrnOverlayPopDirection popDirection;

/// 要展示的内容
final Widget content;
final Widget? content;

const BrnOverlayWindow({
this.context,
this.targetKey,
required this.context,
required this.targetKey,
this.popDirection = BrnOverlayPopDirection.bottom,
this.content,
});
Expand All @@ -41,27 +38,27 @@ class BrnOverlayWindow extends StatefulWidget {
/// [targetKey] 锚点 Widget 的 key,用于 OverlayWindow 的定位
/// [popDirection] OverlayWindow 相对于 key 的展示位置, 默认 bottom
/// [content] 要展示的内容
/// [autoDismissOnTouchOutSide] 点击 OverlayWindow 外部是否自动消失
/// [autoDismissOnTouchOutSide] 点击 OverlayWindow 外部是否自动消失,默认为 true
/// [onDismiss] OverlayWindow 消失回调
static BrnOverlayController showOverlayWindow(
BuildContext context, Key targetKey,
{Widget content,
BrnOverlayPopDirection popDirection,
bool autoDismissOnTouchOutSide,
Function onDismiss}) {
static BrnOverlayController? showOverlayWindow(
BuildContext context, GlobalKey? targetKey,
{Widget? content,
BrnOverlayPopDirection popDirection = BrnOverlayPopDirection.bottom,
bool autoDismissOnTouchOutSide = true,
Function? onDismiss}) {
assert(content != null);
assert(targetKey != null);
assert(content != null);

if (content == null || targetKey == null) return null;

BrnOverlayController overlayController;
BrnOverlayController? overlayController;
OverlayEntry entry = OverlayEntry(builder: (context) {
return GestureDetector(
behavior: (autoDismissOnTouchOutSide ?? true)
behavior: (autoDismissOnTouchOutSide)
? HitTestBehavior.opaque
: HitTestBehavior.deferToChild,
onTap: (autoDismissOnTouchOutSide ?? true)
onTap: (autoDismissOnTouchOutSide)
? () {
overlayController?.removeOverlay();
if (onDismiss != null) {
Expand All @@ -84,13 +81,16 @@ class BrnOverlayWindow extends StatefulWidget {

class _BrnOverlayWindowState extends State<BrnOverlayWindow> {
/// targetView的位置
Rect _showRect;
Rect? _showRect;

/// 屏幕的尺寸
Size _screenSize;
late Size _screenSize;

/// overlay 在targetView 四周的偏移位置
double _left, _right, _top, _bottom;
double _left = 0;
double _right = 0;
double _top = 0;
double _bottom = 0;

/// targetView 的 Size
Size _targetViewSize = Size.zero;
Expand All @@ -99,11 +99,14 @@ class _BrnOverlayWindowState extends State<BrnOverlayWindow> {
Widget build(BuildContext context) {
this._showRect = _getWidgetGlobalRect(widget.targetKey);
this._screenSize = window.physicalSize / window.devicePixelRatio;
_calculateOffset();
return _buildContent();
if (this._showRect == null) {
return Container();
}
_calculateOffset(this._showRect!);
return _buildContent(this._showRect!);
}

Widget _buildContent() {
Widget _buildContent(Rect showRectNonNull) {
var contentPart = Material(
color: Colors.transparent,
child: MeasureSize(
Expand All @@ -112,19 +115,19 @@ class _BrnOverlayWindowState extends State<BrnOverlayWindow> {
_targetViewSize = size;
});
},
child: widget.content));
child: widget.content ?? Container()));
var placeHolderPart = GestureDetector();
Widget realContent;

double marginTop =
_showRect.top + (_showRect.height - _targetViewSize.height) / 2;
showRectNonNull.top + (showRectNonNull.height - _targetViewSize.height) / 2;
if (_screenSize.height - marginTop < _targetViewSize.height) {
marginTop = max(0, _screenSize.height - _targetViewSize.height);
}
marginTop = max(0, marginTop);

double marginLeft =
_showRect.left + (_showRect.width - _targetViewSize.width) / 2;
showRectNonNull.left + (showRectNonNull.width - _targetViewSize.width) / 2;
if (_screenSize.width - marginLeft < _targetViewSize.width) {
marginLeft = max(0, _screenSize.width - _targetViewSize.width);
}
Expand Down Expand Up @@ -202,36 +205,40 @@ class _BrnOverlayWindowState extends State<BrnOverlayWindow> {
///
/// 获取targetView的位置
///
Rect _getWidgetGlobalRect(GlobalKey key) {
if (key == null) {
return null;
Rect? _getWidgetGlobalRect(GlobalKey key) {
BuildContext? ctx = key.currentContext;
RenderObject? obj;
if (ctx != null) {
obj = ctx.findRenderObject();
}
RenderBox renderBox = key.currentContext.findRenderObject();
var offset = renderBox.localToGlobal(Offset.zero);
return Rect.fromLTWH(
offset.dx, offset.dy, renderBox.size.width, renderBox.size.height);
if (obj != null && obj is RenderBox) {
RenderBox renderBox = obj;
var offset = renderBox.localToGlobal(Offset.zero);
return Rect.fromLTWH(
offset.dx, offset.dy, renderBox.size.width, renderBox.size.height);
}
return null;
}

///
/// 计算popUpWindow显示的位置
///
void _calculateOffset() {
_right = _left = _top = _bottom = 0;
void _calculateOffset(Rect showRectNonNull) {
if (widget.popDirection == BrnOverlayPopDirection.left) {
_left = _showRect.left;
_left = showRectNonNull.left;
} else if (widget.popDirection == BrnOverlayPopDirection.right) {
_right = _showRect.right;
_right = showRectNonNull.right;
} else if (widget.popDirection == BrnOverlayPopDirection.bottom) {
_bottom = _showRect.bottom;
_bottom = showRectNonNull.bottom;
} else if (widget.popDirection == BrnOverlayPopDirection.top) {
// 在targetView上方
_top = _showRect.top;
_top = showRectNonNull.top;
}
}
}

class BrnOverlayController {
OverlayEntry _entry;
OverlayEntry? _entry;

BuildContext context;
bool _isOverlayShowing = false;
Expand All @@ -241,8 +248,10 @@ class BrnOverlayController {
BrnOverlayController._(this.context, this._entry);

showOverlay() {
Overlay.of(context).insert(_entry);
_isOverlayShowing = true;
if (_entry != null) {
Overlay.of(context)?.insert(_entry!);
_isOverlayShowing = true;
}
}

void removeOverlay() {
Expand Down
Loading

0 comments on commit 8427312

Please sign in to comment.