Skip to content

Instantly share code, notes, and snippets.

@PlugFox
Last active September 20, 2023 10:42
Show Gist options
  • Save PlugFox/60f41eb9f76d5513df0bfe80f9681271 to your computer and use it in GitHub Desktop.
Save PlugFox/60f41eb9f76d5513df0bfe80f9681271 to your computer and use it in GitHub Desktop.
Date time in chat bubbles example
/*
* https://gist.github.com/PlugFox/60f41eb9f76d5513df0bfe80f9681271
* https://dartpad.dev/?id=60f41eb9f76d5513df0bfe80f9681271
*/
import 'package:flutter/widgets.dart';
void main() => runApp(const App());
class App extends StatelessWidget {
const App({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) => Directionality(
textDirection: TextDirection.ltr,
child: DefaultTextStyle(
style: const TextStyle(
fontSize: 18,
color: Color(0xFF000000),
),
child: LayoutBuilder(
builder: (context, _) => MediaQuery(
data: MediaQueryData.fromWindow(WidgetsBinding.instance!.window),
child: Builder(
builder: (context) => Stack(
alignment: Alignment.topCenter,
children: <Widget>[
Positioned.fill(
child: Center(
child: ConstrainedBox(
constraints: const BoxConstraints(
maxWidth: 300,
),
child: SingleChildScrollView(
child: Padding(
padding: EdgeInsets.symmetric(vertical: MediaQuery.of(context).padding.top + 80),
child: Column(
children: <Widget>[
ChatBubble.left(),
ChatBubble.right(),
],
),
),
),
),
),
),
Positioned(
top: MediaQuery.of(context).padding.top + 12,
height: 65,
child: ConstrainedBox(
constraints: const BoxConstraints(
maxWidth: 350,
),
child: const ChatInputField(),
),
),
],
),
),
),
),
),
);
}
class ChatBubble extends StatelessWidget {
static const double _topPadding = 6;
static const double _sidePadding = 24;
final Alignment alignment;
final Color background;
final EdgeInsets padding;
const ChatBubble._({
required this.alignment,
required this.background,
required this.padding,
Key? key,
}) : super(key: key);
factory ChatBubble.left({Key? key}) => ChatBubble._(
alignment: Alignment.topLeft,
background: const Color(0xFFef5350),
padding: const EdgeInsets.only(
top: _topPadding,
right: _sidePadding,
),
key: key,
);
factory ChatBubble.right({Key? key}) => ChatBubble._(
alignment: Alignment.topRight,
background: const Color(0xFF42a5f5),
padding: const EdgeInsets.only(
top: _topPadding,
left: _sidePadding,
),
key: key,
);
@override
Widget build(BuildContext context) => Padding(
padding: padding,
child: Align(
alignment: alignment,
child: DecoratedBox(
position: DecorationPosition.background,
decoration: BoxDecoration(
color: background,
borderRadius: BorderRadius.circular(5),
),
child: Padding(
padding: const EdgeInsets.symmetric(
horizontal: 8,
vertical: 5,
),
child: ValueListenableBuilder<TextEditingValue>(
valueListenable: ChatInputField.controller,
builder: (context, value, _) => Stack(
children: <Widget>[
RichText(
text: TextSpan(
children: <InlineSpan>[
TextSpan(
text: value.text,
style: DefaultTextStyle.of(context).style,
),
const WidgetSpan(
child: Opacity(
opacity: 0,
child: DateTimeWidget(),
),
),
],
),
),
const Positioned.fill(
child: Align(
alignment: Alignment.bottomRight,
child: DateTimeWidget(),
),
),
],
),
),
),
),
),
);
}
class DateTimeWidget extends StatelessWidget {
static String _evalDateTime() {
final now = DateTime.now();
return '${now.hour}:${now.minute.toString().padLeft(2, '0')}:${now.second.toString().padLeft(2, '0')}';
}
static final Stream<String> _timeStream =
Stream<void>.periodic(const Duration(seconds: 1)).map<String>((_) => _evalDateTime()).asBroadcastStream();
const DateTimeWidget({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) => Padding(
padding: const EdgeInsets.only(left: 8),
child: StreamBuilder<String>(
initialData: _evalDateTime(),
stream: _timeStream,
builder: (context, snapshot) => Text(
snapshot.data ?? '',
style: DefaultTextStyle.of(context).style.copyWith(
color: const Color(0xFF263238),
fontSize: 12,
),
),
),
);
}
class ChatInputField extends StatelessWidget {
static final TextEditingController controller = TextEditingController(text: 'Hello world!');
static final FocusNode _focusNode = FocusNode();
const ChatInputField({
Key? key,
}) : super(key: key);
@override
Widget build(BuildContext context) => DecoratedBox(
position: DecorationPosition.background,
decoration: BoxDecoration(
color: const Color(0xFFcfd8dc),
borderRadius: BorderRadius.circular(5),
),
child: Align(
alignment: Alignment.centerLeft,
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
child: EditableText(
controller: controller,
focusNode: _focusNode,
backgroundCursorColor: const Color(0xFF000000),
cursorColor: const Color(0xFF000000),
style: DefaultTextStyle.of(context).style,
textAlign: TextAlign.left,
minLines: 1,
maxLines: 2,
keyboardType: TextInputType.multiline,
onSubmitted: (_) => FocusScope.of(context).unfocus(),
),
),
),
);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment