A Focusable and Blurable TextField Widget in Flutter
Flutter Focus Widget
A focusable and blurable widget of based on the FocusNode.
When the FocusWidget had focusTap this FocusWidget outside area will call the FocusNode’s unfocus method and trigger the FocusNode’s listener
Example

import 'package:flutter/cupertino.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/widgets.dart';
typedef FocusNodeBuilder = Widget Function(
BuildContext context, FocusNode focusNode);
typedef OnLostFocus = void Function(Widget widget, FocusNode focusNode);
class FocusWidget extends StatefulWidget {
final FocusNode focusNode;
final Widget child;
final OnLostFocus? onLostFocus;
final bool showFocusArea;
const FocusWidget({
Key? key,
required this.child,
required this.focusNode,
this.showFocusArea = false,
this.onLostFocus,
}) : super(key: key);
@override
FocusWidgetState createState() => FocusWidgetState();
static FocusWidget builder(
BuildContext context, {
required FocusNodeBuilder builder,
showFocusArea = false,
OnLostFocus? onLostFocus,
}) {
// print('builder area $showFocusArea');
final focusNode = FocusNode();
return FocusWidget(
focusNode: focusNode,
showFocusArea: showFocusArea,
onLostFocus: onLostFocus,
child: builder(
context,
focusNode,
),
);
}
}
class FocusWidgetState extends State<FocusWidget> {
OverlayEntry? _overlayEntry;
late Rect rect;
@override
void initState() {
// print('state init');
widget.focusNode.addListener(update);
super.initState();
}
@override
void didUpdateWidget(FocusWidget oldWidget) {
// print('didUpdateWidget');
widget.focusNode.removeListener(update);
widget.focusNode.addListener(update);
super.didUpdateWidget(oldWidget);
}
@override
void dispose() {
widget.focusNode.removeListener(update);
super.dispose();
}
void update() {
final renderBox = context.findRenderObject() as RenderBox;
final size = renderBox.size;
final offset = renderBox.localToGlobal(Offset.zero);
rect = Rect.fromLTWH(offset.dx, offset.dy, size.width, size.height);
_createOverlay();
}
void _removeOverlay() {
_overlayEntry?.remove();
_overlayEntry = null;
}
void _createOverlay() {
_removeOverlay();
if (widget.focusNode.hasFocus) {
// print('has focus');
final children = <Widget>[
Listener(
behavior: HitTestBehavior.translucent,
onPointerMove: (e) {
final scope = FocusScope.of(context);
scope.unfocus();
},
onPointerUp: (e) {
// print('onPointerDown');
if (rect.contains(e.position) == false) {
// print('超出');
final scope = FocusScope.of(context);
scope.unfocus();
if (widget.onLostFocus != null)
widget.onLostFocus?.call(widget.child, widget.focusNode);
_removeOverlay();
}
},
),
];
if (widget.showFocusArea) {
children.insert(
0,
Positioned.fromRect(
rect: rect,
child: Container(
color: Color(0x60ff0000),
),
));
}
_overlayEntry = new OverlayEntry(
builder: (context) {
return Stack(
children: children,
);
},
);
Overlay.of(context)?.insert(_overlayEntry!);
}
}
@override
Widget build(BuildContext context) {
return NotificationListener<LayoutChangedNotification>(
child: widget.child,
onNotification: (LayoutChangedNotification notification) {
// print('notification $notification');
update();
return true;
},
);
}
}
Credits:
This is developed by gzlock
Download this project from the below link.
https://github.com/gzlock/flutter_focus_widget/archive/refs/heads/master.zip
You can visit original source page from the below link:
https://github.com/gzlock/flutter_focus_widget
