changed over to the new overlays

This commit is contained in:
Deven Joshi
2021-09-03 16:34:47 +05:30
parent e0d0f8974f
commit 0a384f3c45
2 changed files with 255 additions and 167 deletions
@@ -0,0 +1,140 @@
import 'package:flutter/material.dart';
class AnchoredOverlay extends StatelessWidget {
final bool showOverlay;
final Widget Function(BuildContext, Offset anchor) overlayBuilder;
final Widget child;
AnchoredOverlay({
this.showOverlay = false,
required this.overlayBuilder,
required this.child,
});
@override
Widget build(BuildContext context) {
return new Container(
child: new LayoutBuilder(
builder: (BuildContext context, BoxConstraints constraints) {
return new OverlayBuilder(
showOverlay: showOverlay,
overlayBuilder: (BuildContext overlayContext) {
RenderBox box = context.findRenderObject() as RenderBox;
final center =
box.size.center(box.localToGlobal(const Offset(0.0, 0.0)));
return overlayBuilder(overlayContext, center);
},
child: child,
);
}),
);
}
}
class OverlayBuilder extends StatefulWidget {
final bool showOverlay;
final Widget Function(BuildContext) overlayBuilder;
final Widget child;
OverlayBuilder({
this.showOverlay = false,
required this.overlayBuilder,
required this.child,
});
@override
_OverlayBuilderState createState() => new _OverlayBuilderState();
}
class _OverlayBuilderState extends State<OverlayBuilder> {
OverlayEntry? overlayEntry;
@override
void initState() {
super.initState();
if (widget.showOverlay) {
WidgetsBinding.instance?.addPostFrameCallback((_) => showOverlay());
}
}
@override
void didUpdateWidget(OverlayBuilder oldWidget) {
super.didUpdateWidget(oldWidget);
WidgetsBinding.instance
?.addPostFrameCallback((_) => syncWidgetAndOverlay());
}
@override
void reassemble() {
super.reassemble();
WidgetsBinding.instance
?.addPostFrameCallback((_) => syncWidgetAndOverlay());
}
@override
void dispose() {
if (isShowingOverlay()) {
hideOverlay();
}
super.dispose();
}
bool isShowingOverlay() => overlayEntry != null;
void showOverlay() {
overlayEntry = new OverlayEntry(
builder: widget.overlayBuilder,
);
if (overlayEntry != null) {
addToOverlay(overlayEntry!);
}
}
void addToOverlay(OverlayEntry entry) async {
print('addToOverlay');
Overlay.of(context)?.insert(entry);
}
void hideOverlay() {
print('hideOverlay');
overlayEntry?.remove();
overlayEntry = null;
}
void syncWidgetAndOverlay() {
if (isShowingOverlay() && !widget.showOverlay) {
hideOverlay();
} else if (!isShowingOverlay() && widget.showOverlay) {
showOverlay();
}
}
@override
Widget build(BuildContext context) {
return widget.child;
}
}
class CenterAbout extends StatelessWidget {
final Offset position;
final Widget child;
CenterAbout({
required this.position,
required this.child,
});
@override
Widget build(BuildContext context) {
return new Positioned(
top: position.dy,
left: position.dx,
child: new FractionalTranslation(
translation: const Offset(-0.5, -0.5),
child: child,
),
);
}
}
@@ -11,6 +11,7 @@ import 'package:flutter_svg/flutter_svg.dart';
import 'package:image_picker/image_picker.dart'; import 'package:image_picker/image_picker.dart';
import 'package:photo_manager/photo_manager.dart'; import 'package:photo_manager/photo_manager.dart';
import 'package:shimmer/shimmer.dart'; import 'package:shimmer/shimmer.dart';
import 'package:stream_chat_flutter/overlays.dart';
import 'package:stream_chat_flutter/src/emoji/emoji.dart'; import 'package:stream_chat_flutter/src/emoji/emoji.dart';
import 'package:stream_chat_flutter/src/extension.dart'; import 'package:stream_chat_flutter/src/extension.dart';
import 'package:stream_chat_flutter/src/media_list_view.dart'; import 'package:stream_chat_flutter/src/media_list_view.dart';
@@ -318,7 +319,7 @@ class MessageInputState extends State<MessageInput> {
late final FocusNode _focusNode; late final FocusNode _focusNode;
bool _inputEnabled = true; bool _inputEnabled = true;
bool _commandEnabled = false; bool _commandEnabled = false;
OverlayEntry? _commandsOverlay, _mentionsOverlay, _emojiOverlay; Widget? _commandsOverlay, _mentionsOverlay, _emojiOverlay;
late Iterable<String> _emojiNames; late Iterable<String> _emojiNames;
Command? _chosenCommand; Command? _chosenCommand;
@@ -469,7 +470,21 @@ class MessageInputState extends State<MessageInput> {
child: child, child: child,
); );
} }
return child; return AnchoredOverlay(
showOverlay: _commandsOverlay != null ||
_emojiOverlay != null ||
_mentionsOverlay != null,
overlayBuilder: (context, offset) {
if (_commandsOverlay != null) {
return _buildCommandsOverlayEntry();
} else if (_emojiOverlay != null) {
return _buildEmojiOverlay();
} else {
return _buildMentionsOverlayEntry();
}
},
child: child,
);
} }
Flex _buildTextField(BuildContext context) => Flex( Flex _buildTextField(BuildContext context) => Flex(
@@ -811,11 +826,8 @@ class MessageInputState extends State<MessageInput> {
1); 1);
}); });
_commandsOverlay?.remove();
_commandsOverlay = null; _commandsOverlay = null;
_mentionsOverlay?.remove();
_mentionsOverlay = null; _mentionsOverlay = null;
_emojiOverlay?.remove();
_emojiOverlay = null; _emojiOverlay = null;
_checkCommands(s.trim(), context); _checkCommands(s.trim(), context);
@@ -861,9 +873,9 @@ class MessageInputState extends State<MessageInput> {
} else { } else {
_emojiOverlay = _buildEmojiOverlay(); _emojiOverlay = _buildEmojiOverlay();
if (_emojiOverlay != null) { // if (_emojiOverlay != null) {
Overlay.of(context)!.insert(_emojiOverlay!); // Overlay.of(context)!.insert(_emojiOverlay!);
} // }
} }
} }
} }
@@ -877,9 +889,6 @@ class MessageInputState extends State<MessageInput> {
.last .last
.contains('@')) { .contains('@')) {
_mentionsOverlay = _buildMentionsOverlayEntry(); _mentionsOverlay = _buildMentionsOverlayEntry();
if (_mentionsOverlay != null) {
Overlay.of(context)!.insert(_mentionsOverlay!);
}
} }
} }
@@ -899,18 +908,14 @@ class MessageInputState extends State<MessageInput> {
setState(() { setState(() {
_commandEnabled = true; _commandEnabled = true;
}); });
_commandsOverlay?.remove();
_commandsOverlay = null; _commandsOverlay = null;
} else { } else {
_commandsOverlay = _buildCommandsOverlayEntry(); _commandsOverlay = _buildCommandsOverlayEntry();
if (_commandsOverlay != null) {
Overlay.of(context)!.insert(_commandsOverlay!);
}
} }
} }
} }
OverlayEntry? _buildCommandsOverlayEntry() { Widget _buildCommandsOverlayEntry() {
final text = textEditingController.text.trimLeft(); final text = textEditingController.text.trimLeft();
final commands = StreamChannel.of(context) final commands = StreamChannel.of(context)
.channel .channel
@@ -920,15 +925,7 @@ class MessageInputState extends State<MessageInput> {
.toList() ?? .toList() ??
[]; [];
if (commands.isEmpty) { return Padding(
return null;
}
// ignore: cast_nullable_to_non_nullable
final renderBox = context.findRenderObject() as RenderBox;
final size = renderBox.size;
final child = Padding(
padding: const EdgeInsets.all(8), padding: const EdgeInsets.all(8),
child: Card( child: Card(
elevation: 2, elevation: 2,
@@ -1020,22 +1017,6 @@ class MessageInputState extends State<MessageInput> {
), ),
), ),
); );
return OverlayEntry(
builder: (context) => Positioned(
bottom: size.height + MediaQuery.of(context).viewInsets.bottom,
left: 0,
right: 0,
child: TweenAnimationBuilder<double>(
tween: Tween(begin: 0, end: 1),
duration: const Duration(milliseconds: 300),
curve: Curves.easeInOutExpo,
builder: (context, val, child) => Transform.scale(
scale: val,
child: child,
),
child: child,
),
));
} }
Widget _buildFilePickerSection() { Widget _buildFilePickerSection() {
@@ -1323,7 +1304,7 @@ class MessageInputState extends State<MessageInput> {
} }
} }
OverlayEntry? _buildMentionsOverlayEntry() { Widget _buildMentionsOverlayEntry() {
final splits = textEditingController.text final splits = textEditingController.text
.substring(0, textEditingController.value.selection.start) .substring(0, textEditingController.value.selection.start)
.split('@'); .split('@');
@@ -1343,10 +1324,6 @@ class MessageInputState extends State<MessageInput> {
.toList() ?? .toList() ??
[]; [];
if (members.isEmpty) {
return null;
}
// ignore: cast_nullable_to_non_nullable // ignore: cast_nullable_to_non_nullable
final renderBox = context.findRenderObject() as RenderBox; final renderBox = context.findRenderObject() as RenderBox;
final size = renderBox.size; final size = renderBox.size;
@@ -1396,7 +1373,6 @@ class MessageInputState extends State<MessageInput> {
), ),
); );
_debounce!.cancel(); _debounce!.cancel();
_mentionsOverlay?.remove();
_mentionsOverlay = null; _mentionsOverlay = null;
}, },
child: widget.mentionsTileBuilder != null child: widget.mentionsTileBuilder != null
@@ -1414,133 +1390,119 @@ class MessageInputState extends State<MessageInput> {
), ),
), ),
); );
return OverlayEntry( return Positioned(
builder: (context) => Positioned( bottom: size.height + MediaQuery.of(context).viewInsets.bottom,
bottom: size.height + MediaQuery.of(context).viewInsets.bottom, left: 0,
left: 0, right: 0,
right: 0, child: TweenAnimationBuilder<double>(
child: TweenAnimationBuilder<double>( tween: Tween(begin: 0, end: 1),
tween: Tween(begin: 0, end: 1), duration: const Duration(milliseconds: 300),
duration: const Duration(milliseconds: 300), curve: Curves.easeInOutExpo,
curve: Curves.easeInOutExpo, builder: (context, val, child) => Transform.scale(
builder: (context, val, child) => Transform.scale( scale: val,
scale: val,
child: child,
),
child: child, child: child,
), ),
child: child,
), ),
); );
} }
OverlayEntry? _buildEmojiOverlay() { Widget _buildEmojiOverlay() {
final splits = textEditingController.text final splits = textEditingController.text
.substring(0, textEditingController.value.selection.start) .substring(0, textEditingController.value.selection.start)
.split(':'); .split(':');
final query = splits.last.toLowerCase(); final query = splits.last.toLowerCase();
if (query.isEmpty) {
return null;
}
final emojis = _emojiNames final emojis = _emojiNames
.where((e) => e.contains(query)) .where((e) => e.contains(query))
.map(Emoji.byName) .map(Emoji.byName)
.where((e) => e != null); .where((e) => e != null);
if (emojis.isEmpty) {
return null;
}
// ignore: cast_nullable_to_non_nullable // ignore: cast_nullable_to_non_nullable
final renderBox = context.findRenderObject() as RenderBox; final renderBox = context.findRenderObject() as RenderBox;
final size = renderBox.size; final size = renderBox.size;
return OverlayEntry( return Positioned(
builder: (context) => Positioned( bottom: size.height + MediaQuery.of(context).viewInsets.bottom,
bottom: size.height + MediaQuery.of(context).viewInsets.bottom, left: 0,
left: 0, right: 0,
right: 0, child: Card(
child: Card( margin: const EdgeInsets.all(8),
margin: const EdgeInsets.all(8), elevation: 2,
elevation: 2, color: _streamChatTheme.colorTheme.barsBg,
color: _streamChatTheme.colorTheme.barsBg, shape: RoundedRectangleBorder(
shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(8),
borderRadius: BorderRadius.circular(8), ),
), clipBehavior: Clip.hardEdge,
clipBehavior: Clip.hardEdge, child: Container(
child: Container( constraints: BoxConstraints.loose(const Size.fromHeight(200)),
constraints: BoxConstraints.loose(const Size.fromHeight(200)), decoration: BoxDecoration(
decoration: BoxDecoration( boxShadow: const [
boxShadow: const [ BoxShadow(
BoxShadow( spreadRadius: -8,
spreadRadius: -8, blurRadius: 5,
blurRadius: 5, offset: Offset(0, -4),
offset: Offset(0, -4),
),
],
color: _streamChatTheme.colorTheme.barsBg,
),
child: ListView.builder(
padding: const EdgeInsets.all(0),
shrinkWrap: true,
itemCount: emojis.length + 1,
itemBuilder: (context, i) {
if (i == 0) {
return Padding(
padding: const EdgeInsets.only(left: 8, top: 8),
child: Row(
children: [
Padding(
padding:
const EdgeInsets.symmetric(horizontal: 8),
child: StreamSvgIcon.smile(
color: _streamChatTheme
.colorTheme.accentPrimary,
),
),
Flexible(
child: Text(
context.translations.emojiMatchingQueryText(
query,
),
style: TextStyle(
color: _streamChatTheme
.colorTheme.textHighEmphasis
.withOpacity(.5),
),
),
)
],
),
);
}
final emoji = emojis.elementAt(i - 1)!;
final themeData = Theme.of(context);
return ListTile(
title: SubstringHighlight(
text:
// ignore: lines_longer_than_80_chars
"${emoji.char} ${emoji.name!.replaceAll('_', ' ')}",
term: query,
textStyleHighlight:
themeData.textTheme.headline6!.copyWith(
fontSize: 14.5,
fontWeight: FontWeight.bold,
),
textStyle: themeData.textTheme.headline6!.copyWith(
fontSize: 14.5,
),
),
onTap: () {
_chooseEmoji(splits, emoji);
},
);
}),
),
), ),
)); ],
color: _streamChatTheme.colorTheme.barsBg,
),
child: ListView.builder(
padding: const EdgeInsets.all(0),
shrinkWrap: true,
itemCount: emojis.length + 1,
itemBuilder: (context, i) {
if (i == 0) {
return Padding(
padding: const EdgeInsets.only(left: 8, top: 8),
child: Row(
children: [
Padding(
padding: const EdgeInsets.symmetric(horizontal: 8),
child: StreamSvgIcon.smile(
color: _streamChatTheme.colorTheme.accentPrimary,
),
),
Flexible(
child: Text(
context.translations.emojiMatchingQueryText(
query,
),
style: TextStyle(
color: _streamChatTheme.colorTheme.textHighEmphasis
.withOpacity(.5),
),
),
)
],
),
);
}
final emoji = emojis.elementAt(i - 1)!;
final themeData = Theme.of(context);
return ListTile(
title: SubstringHighlight(
text:
// ignore: lines_longer_than_80_chars
"${emoji.char} ${emoji.name!.replaceAll('_', ' ')}",
term: query,
textStyleHighlight: themeData.textTheme.headline6!.copyWith(
fontSize: 14.5,
fontWeight: FontWeight.bold,
),
textStyle: themeData.textTheme.headline6!.copyWith(
fontSize: 14.5,
),
),
onTap: () {
_chooseEmoji(splits, emoji);
},
);
},
),
),
),
);
} }
void _chooseEmoji(List<String> splits, Emoji emoji) { void _chooseEmoji(List<String> splits, Emoji emoji) {
@@ -1555,7 +1517,6 @@ class MessageInputState extends State<MessageInput> {
), ),
); );
_emojiOverlay?.remove();
_emojiOverlay = null; _emojiOverlay = null;
} }
@@ -1565,7 +1526,6 @@ class MessageInputState extends State<MessageInput> {
_chosenCommand = c; _chosenCommand = c;
_commandEnabled = true; _commandEnabled = true;
}); });
_commandsOverlay?.remove();
_commandsOverlay = null; _commandsOverlay = null;
} }
@@ -1778,13 +1738,9 @@ class MessageInputState extends State<MessageInput> {
if (_commandsOverlay == null) { if (_commandsOverlay == null) {
setState(() { setState(() {
_commandsOverlay = _buildCommandsOverlayEntry(); _commandsOverlay = _buildCommandsOverlayEntry();
if (_commandsOverlay != null) {
Overlay.of(context)!.insert(_commandsOverlay!);
}
}); });
} else { } else {
setState(() { setState(() {
_commandsOverlay?.remove();
_commandsOverlay = null; _commandsOverlay = null;
}); });
} }
@@ -1809,11 +1765,8 @@ class MessageInputState extends State<MessageInput> {
), ),
splashRadius: 24, splashRadius: 24,
onPressed: () async { onPressed: () async {
_emojiOverlay?.remove();
_emojiOverlay = null; _emojiOverlay = null;
_commandsOverlay?.remove();
_commandsOverlay = null; _commandsOverlay = null;
_mentionsOverlay?.remove();
_mentionsOverlay = null; _mentionsOverlay = null;
if (_openFilePickerSection) { if (_openFilePickerSection) {
@@ -2100,9 +2053,7 @@ class MessageInputState extends State<MessageInput> {
_commandEnabled = false; _commandEnabled = false;
}); });
_commandsOverlay?.remove();
_commandsOverlay = null; _commandsOverlay = null;
_mentionsOverlay?.remove();
_mentionsOverlay = null; _mentionsOverlay = null;
Message message; Message message;
@@ -2246,9 +2197,6 @@ class MessageInputState extends State<MessageInput> {
@override @override
void dispose() { void dispose() {
_commandsOverlay?.remove();
_emojiOverlay?.remove();
_mentionsOverlay?.remove();
_keyboardListener?.cancel(); _keyboardListener?.cancel();
textEditingController.dispose(); textEditingController.dispose();
_stopSlowMode(); _stopSlowMode();