fix(search): fix history overlay flicker and clear/delete behavior
This commit is contained in:
1 parent
20d5d4d694
commit
28f1dfb1b0
4 files changed
+529
-169
No files matched your search
@@ -22,7 +22,21 @@ class SearchHistoryNotifier extends Notifier<List<String>> {
|
||||
final data = prefs.getString(_searchHistoryKey);
|
||||
if (data != null) {
|
||||
final List<dynamic> decoded = jsonDecode(data);
|
||||
state = decoded.map((e) => e.toString()).toList();
|
||||
final loaded = decoded
|
||||
.map((e) => e.toString().trim())
|
||||
.where((e) => e.isNotEmpty)
|
||||
.toList();
|
||||
|
||||
final merged = [...state];
|
||||
for (final item in loaded) {
|
||||
if (!merged.contains(item)) {
|
||||
merged.add(item);
|
||||
}
|
||||
}
|
||||
if (merged.length > 10) {
|
||||
merged.removeRange(10, merged.length);
|
||||
}
|
||||
state = merged;
|
||||
}
|
||||
} catch (e) {
|
||||
// Ignore errors on load
|
||||
@@ -49,15 +63,20 @@ class SearchHistoryNotifier extends Notifier<List<String>> {
|
||||
}
|
||||
|
||||
Future<void> removeSearch(String query) async {
|
||||
final newList = List<String>.from(state)..remove(query);
|
||||
final trimQuery = query.trim();
|
||||
final newList = List<String>.from(state)..remove(trimQuery);
|
||||
state = newList;
|
||||
await _saveHistory(newList);
|
||||
}
|
||||
|
||||
Future<void> clearHistory() async {
|
||||
state = [];
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
await prefs.remove(_searchHistoryKey);
|
||||
try {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
await prefs.remove(_searchHistoryKey);
|
||||
} catch (e) {
|
||||
// Ignore errors on clear
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _saveHistory(List<String> list) async {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import '../../../core/constants/app_colors.dart';
|
||||
import '../../providers/search_history_provider.dart';
|
||||
@@ -34,17 +35,39 @@ class AppSearchBar extends ConsumerStatefulWidget {
|
||||
}
|
||||
|
||||
class _AppSearchBarState extends ConsumerState<AppSearchBar> {
|
||||
late final TextEditingController _controller;
|
||||
late final FocusNode _focusNode;
|
||||
late TextEditingController _controller;
|
||||
late FocusNode _focusNode;
|
||||
bool _hasText = false;
|
||||
bool _isExpanded = false;
|
||||
|
||||
OverlayEntry? _overlayEntry;
|
||||
final LayerLink _layerLink = LayerLink();
|
||||
final Object _tapRegionGroupId = Object();
|
||||
bool _isOverlayOpen = false;
|
||||
|
||||
@override
|
||||
void didUpdateWidget(AppSearchBar oldWidget) {
|
||||
super.didUpdateWidget(oldWidget);
|
||||
if (oldWidget.controller != widget.controller) {
|
||||
if (oldWidget.controller == null) {
|
||||
_controller.removeListener(_onTextChanged);
|
||||
_controller.dispose();
|
||||
} else {
|
||||
oldWidget.controller!.removeListener(_onTextChanged);
|
||||
}
|
||||
_controller = widget.controller ?? TextEditingController();
|
||||
_controller.addListener(_onTextChanged);
|
||||
}
|
||||
if (oldWidget.focusNode != widget.focusNode) {
|
||||
if (oldWidget.focusNode == null) {
|
||||
_focusNode.removeListener(_onFocusChanged);
|
||||
_focusNode.dispose();
|
||||
} else {
|
||||
oldWidget.focusNode!.removeListener(_onFocusChanged);
|
||||
}
|
||||
_focusNode = widget.focusNode ?? FocusNode();
|
||||
_focusNode.addListener(_onFocusChanged);
|
||||
}
|
||||
final currentHasText = _controller.text.isNotEmpty;
|
||||
if (currentHasText != _hasText) {
|
||||
setState(() {
|
||||
@@ -73,6 +96,9 @@ class _AppSearchBarState extends ConsumerState<AppSearchBar> {
|
||||
if (hasText) _isExpanded = true;
|
||||
});
|
||||
}
|
||||
if (_isOverlayOpen) {
|
||||
_overlayEntry?.markNeedsBuild();
|
||||
}
|
||||
}
|
||||
|
||||
void _onFocusChanged() {
|
||||
@@ -89,12 +115,10 @@ class _AppSearchBarState extends ConsumerState<AppSearchBar> {
|
||||
if (_focusNode.hasFocus && widget.enableHistory) {
|
||||
_showOverlay();
|
||||
} else {
|
||||
// Delay removal to allow overlay tap events (InkWell) to fire before the widget is destroyed
|
||||
Future.delayed(const Duration(milliseconds: 200), () {
|
||||
Future.delayed(const Duration(milliseconds: 150), () {
|
||||
if (mounted && !_focusNode.hasFocus) {
|
||||
_removeOverlay();
|
||||
|
||||
// Auto collapse if empty and collapsible
|
||||
if (widget.collapsible && _controller.text.trim().isEmpty) {
|
||||
setState(() => _isExpanded = false);
|
||||
}
|
||||
@@ -104,38 +128,66 @@ class _AppSearchBarState extends ConsumerState<AppSearchBar> {
|
||||
}
|
||||
|
||||
void _showOverlay() {
|
||||
if (_overlayEntry != null) return;
|
||||
if (!widget.enableHistory || !mounted) return;
|
||||
|
||||
final history = ref.read(searchHistoryProvider);
|
||||
if (history.isEmpty) return;
|
||||
|
||||
if (_overlayEntry != null) {
|
||||
_isOverlayOpen = true;
|
||||
_overlayEntry?.markNeedsBuild();
|
||||
return;
|
||||
}
|
||||
|
||||
_isOverlayOpen = true;
|
||||
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
if (!mounted || _overlayEntry != null || !_focusNode.hasFocus) return;
|
||||
if (!mounted ||
|
||||
!_isOverlayOpen ||
|
||||
!_focusNode.hasFocus ||
|
||||
_overlayEntry != null) {
|
||||
return;
|
||||
}
|
||||
|
||||
final overlay = Overlay.maybeOf(context);
|
||||
if (overlay == null) return;
|
||||
|
||||
_overlayEntry = OverlayEntry(
|
||||
builder: (ctx) {
|
||||
final renderBox = context.findRenderObject() as RenderBox?;
|
||||
final actualWidth = renderBox?.size.width;
|
||||
final safeWidth =
|
||||
actualWidth ??
|
||||
(widget.maxWidth == double.infinity ? 400.0 : widget.maxWidth);
|
||||
final actualWidth = (renderBox != null && renderBox.hasSize)
|
||||
? renderBox.size.width
|
||||
: (widget.maxWidth == double.infinity
|
||||
? 400.0
|
||||
: widget.maxWidth);
|
||||
|
||||
return Positioned(
|
||||
width: safeWidth,
|
||||
width: actualWidth,
|
||||
child: CompositedTransformFollower(
|
||||
link: _layerLink,
|
||||
showWhenUnlinked: false,
|
||||
offset: const Offset(0, 48),
|
||||
child: _buildHistoryOverlay(),
|
||||
child: TapRegion(
|
||||
groupId: _tapRegionGroupId,
|
||||
child: _buildHistoryOverlay(),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
Overlay.of(context).insert(_overlayEntry!);
|
||||
overlay.insert(_overlayEntry!);
|
||||
});
|
||||
}
|
||||
|
||||
void _removeOverlay() {
|
||||
_overlayEntry?.remove();
|
||||
_overlayEntry = null;
|
||||
_isOverlayOpen = false;
|
||||
if (_overlayEntry != null) {
|
||||
if (_overlayEntry!.mounted) {
|
||||
_overlayEntry!.remove();
|
||||
}
|
||||
_overlayEntry = null;
|
||||
}
|
||||
}
|
||||
|
||||
Widget _buildHistoryOverlay() {
|
||||
@@ -147,6 +199,15 @@ class _AppSearchBarState extends ConsumerState<AppSearchBar> {
|
||||
final history = ref.watch(searchHistoryProvider);
|
||||
if (history.isEmpty) return const SizedBox.shrink();
|
||||
|
||||
final query = _controller.text.trim().toLowerCase();
|
||||
final displayList = query.isEmpty
|
||||
? history
|
||||
: history
|
||||
.where((item) => item.toLowerCase().contains(query))
|
||||
.toList();
|
||||
|
||||
if (displayList.isEmpty) return const SizedBox.shrink();
|
||||
|
||||
return Material(
|
||||
elevation: 4,
|
||||
color: isDark ? AppColors.darkSurfaceCard : Colors.white,
|
||||
@@ -159,88 +220,154 @@ class _AppSearchBarState extends ConsumerState<AppSearchBar> {
|
||||
color: isDark ? AppColors.darkBorder : Colors.grey.shade200,
|
||||
),
|
||||
),
|
||||
child: ListView.builder(
|
||||
padding: const EdgeInsets.symmetric(vertical: 8),
|
||||
shrinkWrap: true,
|
||||
itemCount: history.length,
|
||||
itemBuilder: (context, index) {
|
||||
final term = history[index];
|
||||
return Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: InkWell(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.fromLTRB(16, 10, 12, 6),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
'Recent Searches',
|
||||
style: TextStyle(
|
||||
fontSize: 11,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: isDark
|
||||
? AppColors.darkTextMuted
|
||||
: Colors.grey.shade500,
|
||||
letterSpacing: 0.3,
|
||||
),
|
||||
),
|
||||
InkWell(
|
||||
borderRadius: BorderRadius.circular(4),
|
||||
canRequestFocus: false,
|
||||
onTap: () {
|
||||
_controller.text = term;
|
||||
if (widget.onSubmitted != null) {
|
||||
widget.onSubmitted!(term);
|
||||
}
|
||||
ref
|
||||
.read(searchHistoryProvider.notifier)
|
||||
.addSearch(term);
|
||||
Future.delayed(
|
||||
const Duration(milliseconds: 50),
|
||||
() {
|
||||
if (mounted) _focusNode.unfocus();
|
||||
},
|
||||
);
|
||||
.clearHistory();
|
||||
_removeOverlay();
|
||||
if (!_focusNode.hasFocus) {
|
||||
_focusNode.requestFocus();
|
||||
}
|
||||
},
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 16,
|
||||
vertical: 10,
|
||||
horizontal: 6,
|
||||
vertical: 2,
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(
|
||||
Icons.history,
|
||||
size: 18,
|
||||
color: isDark
|
||||
? AppColors.darkTextSecondary
|
||||
: Colors.grey.shade400,
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Expanded(
|
||||
child: Text(
|
||||
term,
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
color: isDark
|
||||
? AppColors.darkTextPrimary
|
||||
: Colors.black87,
|
||||
),
|
||||
child: Text(
|
||||
'Clear all',
|
||||
style: TextStyle(
|
||||
fontSize: 11,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: isDark
|
||||
? AppColors.darkTextSecondary
|
||||
: Colors.grey.shade600,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Divider(
|
||||
height: 1,
|
||||
thickness: 1,
|
||||
color: isDark ? AppColors.darkBorder : Colors.grey.shade200,
|
||||
),
|
||||
Flexible(
|
||||
child: ListView.builder(
|
||||
padding: const EdgeInsets.symmetric(vertical: 4),
|
||||
shrinkWrap: true,
|
||||
itemCount: displayList.length,
|
||||
itemBuilder: (context, index) {
|
||||
final term = displayList[index];
|
||||
return Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: InkWell(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
canRequestFocus: false,
|
||||
onTap: () {
|
||||
final selected = term;
|
||||
_controller.text = selected;
|
||||
_controller.selection = TextSelection.collapsed(
|
||||
offset: selected.length,
|
||||
);
|
||||
setState(() {
|
||||
_hasText = selected.isNotEmpty;
|
||||
});
|
||||
ref
|
||||
.read(searchHistoryProvider.notifier)
|
||||
.addSearch(selected);
|
||||
widget.onChanged?.call(selected);
|
||||
widget.onSubmitted?.call(selected);
|
||||
_removeOverlay();
|
||||
_focusNode.unfocus();
|
||||
},
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 16,
|
||||
vertical: 9,
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(
|
||||
Icons.history,
|
||||
size: 18,
|
||||
color: isDark
|
||||
? AppColors.darkTextSecondary
|
||||
: Colors.grey.shade400,
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Expanded(
|
||||
child: Text(
|
||||
term,
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
color: isDark
|
||||
? AppColors.darkTextPrimary
|
||||
: Colors.black87,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
InkResponse(
|
||||
radius: 16,
|
||||
onTap: () {
|
||||
ref
|
||||
.read(searchHistoryProvider.notifier)
|
||||
.removeSearch(term);
|
||||
_focusNode.requestFocus();
|
||||
},
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 12,
|
||||
vertical: 10,
|
||||
),
|
||||
child: Icon(
|
||||
Icons.close_rounded,
|
||||
size: 16,
|
||||
color: isDark
|
||||
? AppColors.darkTextMuted
|
||||
: Colors.grey.shade400,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
InkResponse(
|
||||
radius: 16,
|
||||
canRequestFocus: false,
|
||||
onTap: () {
|
||||
ref
|
||||
.read(searchHistoryProvider.notifier)
|
||||
.removeSearch(term);
|
||||
if (!_focusNode.hasFocus) {
|
||||
_focusNode.requestFocus();
|
||||
}
|
||||
},
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 12,
|
||||
vertical: 9,
|
||||
),
|
||||
child: Icon(
|
||||
Icons.close_rounded,
|
||||
size: 16,
|
||||
color: isDark
|
||||
? AppColors.darkTextMuted
|
||||
: Colors.grey.shade400,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
@@ -259,17 +386,31 @@ class _AppSearchBarState extends ConsumerState<AppSearchBar> {
|
||||
}
|
||||
|
||||
void _handleSubmit(String val) {
|
||||
if (val.trim().isNotEmpty) {
|
||||
final query = val.trim();
|
||||
if (query.isNotEmpty) {
|
||||
if (widget.enableHistory) {
|
||||
ref.read(searchHistoryProvider.notifier).addSearch(val.trim());
|
||||
ref.read(searchHistoryProvider.notifier).addSearch(query);
|
||||
}
|
||||
widget.onSubmitted?.call(val.trim());
|
||||
_focusNode.unfocus();
|
||||
widget.onSubmitted?.call(query);
|
||||
}
|
||||
_removeOverlay();
|
||||
_focusNode.unfocus();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
ref.listen<List<String>>(searchHistoryProvider, (previous, next) {
|
||||
if (!mounted) return;
|
||||
if (next.isEmpty && _isOverlayOpen) {
|
||||
_removeOverlay();
|
||||
} else if (next.isNotEmpty &&
|
||||
_focusNode.hasFocus &&
|
||||
!_isOverlayOpen &&
|
||||
widget.enableHistory) {
|
||||
_showOverlay();
|
||||
}
|
||||
});
|
||||
|
||||
if (!_isExpanded && widget.collapsible && !_hasText) {
|
||||
return IconButton(
|
||||
onPressed: () {
|
||||
@@ -277,6 +418,9 @@ class _AppSearchBarState extends ConsumerState<AppSearchBar> {
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
if (mounted) {
|
||||
_focusNode.requestFocus();
|
||||
if (widget.enableHistory) {
|
||||
_showOverlay();
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
@@ -287,82 +431,112 @@ class _AppSearchBarState extends ConsumerState<AppSearchBar> {
|
||||
|
||||
final isDark = Theme.of(context).brightness == Brightness.dark;
|
||||
|
||||
return TapRegion(
|
||||
onTapOutside: (event) {
|
||||
if (_focusNode.hasFocus) {
|
||||
_focusNode.unfocus();
|
||||
}
|
||||
return CallbackShortcuts(
|
||||
bindings: {
|
||||
const SingleActivator(LogicalKeyboardKey.escape): () {
|
||||
if (_isOverlayOpen) {
|
||||
_removeOverlay();
|
||||
} else if (_focusNode.hasFocus) {
|
||||
_focusNode.unfocus();
|
||||
}
|
||||
},
|
||||
},
|
||||
child: CompositedTransformTarget(
|
||||
link: _layerLink,
|
||||
child: Container(
|
||||
width: widget.maxWidth,
|
||||
height: 42.0,
|
||||
decoration: BoxDecoration(
|
||||
color: _focusNode.hasFocus
|
||||
? AppColors.primaryGreen.withValues(alpha: 0.05)
|
||||
: Colors.transparent,
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
child: TextField(
|
||||
controller: _controller,
|
||||
focusNode: _focusNode,
|
||||
textInputAction: TextInputAction.search,
|
||||
onSubmitted: _handleSubmit,
|
||||
onChanged: widget.onChanged,
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
color: isDark ? AppColors.darkTextPrimary : Colors.black87,
|
||||
child: TapRegion(
|
||||
groupId: _tapRegionGroupId,
|
||||
onTapOutside: (event) {
|
||||
if (_focusNode.hasFocus) {
|
||||
_focusNode.unfocus();
|
||||
}
|
||||
_removeOverlay();
|
||||
},
|
||||
child: CompositedTransformTarget(
|
||||
link: _layerLink,
|
||||
child: Container(
|
||||
width: widget.maxWidth,
|
||||
height: 42.0,
|
||||
decoration: BoxDecoration(
|
||||
color: _focusNode.hasFocus
|
||||
? AppColors.primaryGreen.withValues(alpha: 0.05)
|
||||
: Colors.transparent,
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
decoration: InputDecoration(
|
||||
hintText: widget.hint,
|
||||
hintStyle: TextStyle(
|
||||
fontSize: 13,
|
||||
color: isDark ? AppColors.darkTextMuted : Colors.grey.shade500,
|
||||
child: TextField(
|
||||
controller: _controller,
|
||||
focusNode: _focusNode,
|
||||
textInputAction: TextInputAction.search,
|
||||
onSubmitted: _handleSubmit,
|
||||
onChanged: widget.onChanged,
|
||||
onTap: () {
|
||||
if (widget.enableHistory) {
|
||||
_showOverlay();
|
||||
}
|
||||
},
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
color: isDark ? AppColors.darkTextPrimary : Colors.black87,
|
||||
),
|
||||
prefixIcon: widget.hideIconWhenExpanded
|
||||
? null
|
||||
: const Icon(
|
||||
Icons.search_rounded,
|
||||
size: 20,
|
||||
color: AppColors.primaryGreen,
|
||||
),
|
||||
prefixIconConstraints: widget.hideIconWhenExpanded
|
||||
? const BoxConstraints(minWidth: 16, minHeight: 0)
|
||||
: const BoxConstraints(minWidth: 42, minHeight: 42),
|
||||
suffixIcon:
|
||||
widget.showClear &&
|
||||
(_hasText ||
|
||||
_controller.text.isNotEmpty ||
|
||||
(widget.collapsible && _isExpanded))
|
||||
? IconButton(
|
||||
icon: Icon(
|
||||
Icons.close_rounded,
|
||||
size: 18,
|
||||
color: isDark ? AppColors.darkTextMuted : Colors.grey.shade500,
|
||||
decoration: InputDecoration(
|
||||
hintText: widget.hint,
|
||||
hintStyle: TextStyle(
|
||||
fontSize: 13,
|
||||
color:
|
||||
isDark ? AppColors.darkTextMuted : Colors.grey.shade500,
|
||||
),
|
||||
prefixIcon: widget.hideIconWhenExpanded
|
||||
? null
|
||||
: const Icon(
|
||||
Icons.search_rounded,
|
||||
size: 20,
|
||||
color: AppColors.primaryGreen,
|
||||
),
|
||||
splashRadius: 16,
|
||||
onPressed: () {
|
||||
_controller.clear();
|
||||
widget.onChanged?.call('');
|
||||
widget.onSubmitted?.call('');
|
||||
setState(() {
|
||||
_hasText = false;
|
||||
});
|
||||
_focusNode.requestFocus();
|
||||
},
|
||||
)
|
||||
: null,
|
||||
filled: false,
|
||||
isDense: true,
|
||||
contentPadding: const EdgeInsets.only(
|
||||
top: 11,
|
||||
bottom: 11,
|
||||
right: 12,
|
||||
prefixIconConstraints: widget.hideIconWhenExpanded
|
||||
? const BoxConstraints(minWidth: 16, minHeight: 0)
|
||||
: const BoxConstraints(minWidth: 42, minHeight: 42),
|
||||
suffixIcon: widget.showClear &&
|
||||
(_hasText ||
|
||||
_controller.text.isNotEmpty ||
|
||||
(widget.collapsible && _isExpanded))
|
||||
? IconButton(
|
||||
focusNode: FocusNode(
|
||||
skipTraversal: true,
|
||||
canRequestFocus: false,
|
||||
),
|
||||
icon: Icon(
|
||||
Icons.close_rounded,
|
||||
size: 18,
|
||||
color: isDark
|
||||
? AppColors.darkTextMuted
|
||||
: Colors.grey.shade500,
|
||||
),
|
||||
splashRadius: 16,
|
||||
onPressed: () {
|
||||
_controller.clear();
|
||||
widget.onChanged?.call('');
|
||||
widget.onSubmitted?.call('');
|
||||
setState(() {
|
||||
_hasText = false;
|
||||
});
|
||||
if (!_focusNode.hasFocus) {
|
||||
_focusNode.requestFocus();
|
||||
}
|
||||
if (widget.enableHistory) {
|
||||
_showOverlay();
|
||||
_overlayEntry?.markNeedsBuild();
|
||||
}
|
||||
},
|
||||
)
|
||||
: null,
|
||||
filled: false,
|
||||
isDense: true,
|
||||
contentPadding: const EdgeInsets.only(
|
||||
top: 11,
|
||||
bottom: 11,
|
||||
right: 12,
|
||||
),
|
||||
border: InputBorder.none,
|
||||
enabledBorder: InputBorder.none,
|
||||
focusedBorder: InputBorder.none,
|
||||
),
|
||||
border: InputBorder.none,
|
||||
enabledBorder: InputBorder.none,
|
||||
focusedBorder: InputBorder.none,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
Reference in new issue
Block a user