diff --git a/lib/widgets/message_list.dart b/lib/widgets/message_list.dart index 88df3a9a45..c23a58aff1 100644 --- a/lib/widgets/message_list.dart +++ b/lib/widgets/message_list.dart @@ -27,9 +27,9 @@ class MessageListPage extends StatefulWidget { const MessageListPage({super.key, required this.narrow}); static Route buildRoute({int? accountId, BuildContext? context, - required Narrow narrow}) { + required Narrow narrow}) { return MaterialAccountWidgetRoute(accountId: accountId, context: context, - page: MessageListPage(narrow: narrow)); + page: MessageListPage(narrow: narrow)); } /// A [ComposeBoxController], if this [MessageListPage] offers a compose box. @@ -67,7 +67,7 @@ class _MessageListPageState extends State { case StreamNarrow(:final streamId): case TopicNarrow(:final streamId): backgroundColor = store.subscriptions[streamId]?.colorSwatch().barBackground - ?? _kUnsubscribedStreamRecipientHeaderColor; + ?? _kUnsubscribedStreamRecipientHeaderColor; // All recipient headers will match this color; remove distracting line // (but are recipient headers even needed for topic narrows?) removeAppBarBottomBorder = true; @@ -80,38 +80,38 @@ class _MessageListPageState extends State { } return Scaffold( - appBar: AppBar(title: MessageListAppBarTitle(narrow: widget.narrow), - backgroundColor: backgroundColor, - shape: removeAppBarBottomBorder - ? const Border() - : null, // i.e., inherit - ), - // TODO question for Vlad: for a stream view, should we set - // [backgroundColor] based on stream color, as in this frame: - // https://www.figma.com/file/1JTNtYo9memgW7vV6d0ygq/Zulip-Mobile?node-id=132%3A9684&mode=dev - // That's not obviously preferred over the default background that - // we matched to the Figma in 21dbae120. See another frame, which uses that: - // https://www.figma.com/file/1JTNtYo9memgW7vV6d0ygq/Zulip-Mobile?node-id=147%3A9088&mode=dev - body: Builder( - builder: (BuildContext context) => Center( - child: Column(children: [ - MediaQuery.removePadding( - // Scaffold knows about the app bar, and so has run this - // BuildContext, which is under `body`, through - // MediaQuery.removePadding with `removeTop: true`. - context: context, - - // The compose box, when present, pads the bottom inset. - // TODO this copies the details of when the compose box is shown; - // if those details get complicated, refactor to avoid copying. - // TODO(#311) If we have a bottom nav, it will pad the bottom - // inset, and this should always be true. - removeBottom: widget.narrow is! AllMessagesNarrow, - - child: Expanded( - child: MessageList(narrow: widget.narrow))), - ComposeBox(controllerKey: _composeBoxKey, narrow: widget.narrow), - ])))); + appBar: AppBar(title: MessageListAppBarTitle(narrow: widget.narrow), + backgroundColor: backgroundColor, + shape: removeAppBarBottomBorder + ? const Border() + : null, // i.e., inherit + ), + // TODO question for Vlad: for a stream view, should we set + // [backgroundColor] based on stream color, as in this frame: + // https://www.figma.com/file/1JTNtYo9memgW7vV6d0ygq/Zulip-Mobile?node-id=132%3A9684&mode=dev + // That's not obviously preferred over the default background that + // we matched to the Figma in 21dbae120. See another frame, which uses that: + // https://www.figma.com/file/1JTNtYo9memgW7vV6d0ygq/Zulip-Mobile?node-id=147%3A9088&mode=dev + body: Builder( + builder: (BuildContext context) => Center( + child: Column(children: [ + MediaQuery.removePadding( + // Scaffold knows about the app bar, and so has run this + // BuildContext, which is under `body`, through + // MediaQuery.removePadding with `removeTop: true`. + context: context, + + // The compose box, when present, pads the bottom inset. + // TODO this copies the details of when the compose box is shown; + // if those details get complicated, refactor to avoid copying. + // TODO(#311) If we have a bottom nav, it will pad the bottom + // inset, and this should always be true. + removeBottom: widget.narrow is! AllMessagesNarrow, + + child: Expanded( + child: MessageList(narrow: widget.narrow))), + ComposeBox(controllerKey: _composeBoxKey, narrow: widget.narrow), + ])))); } } @@ -124,17 +124,17 @@ class MessageListAppBarTitle extends StatelessWidget { // A null [Icon.icon] makes a blank space. final icon = (stream != null) ? iconDataForStream(stream) : null; return Row( - mainAxisSize: MainAxisSize.min, - // TODO(design): The vertical alignment of the stream privacy icon is a bit ad hoc. - // For screenshots of some experiments, see: - // https://github.com/zulip/zulip-flutter/pull/219#discussion_r1281024746 - crossAxisAlignment: CrossAxisAlignment.baseline, - textBaseline: TextBaseline.alphabetic, - children: [ - Icon(size: 16, icon), - const SizedBox(width: 8), - Flexible(child: Text(text)), - ]); + mainAxisSize: MainAxisSize.min, + // TODO(design): The vertical alignment of the stream privacy icon is a bit ad hoc. + // For screenshots of some experiments, see: + // https://github.com/zulip/zulip-flutter/pull/219#discussion_r1281024746 + crossAxisAlignment: CrossAxisAlignment.baseline, + textBaseline: TextBaseline.alphabetic, + children: [ + Icon(size: 16, icon), + const SizedBox(width: 8), + Flexible(child: Text(text)), + ]); } @override @@ -269,34 +269,34 @@ class _MessageListState extends State with PerAccountStoreAwareStat return DefaultTextStyle.merge( // TODO figure out text color -- web is supposedly hsl(0deg 0% 20%), // but seems much darker than that - style: const TextStyle(color: Color.fromRGBO(0, 0, 0, 1)), - // Pad the left and right insets, for small devices in landscape. - child: SafeArea( - // Don't let this be the place we pad the bottom inset. When there's - // no compose box, we want to let the message-list content pad it. - // TODO(#311) Remove as unnecessary if we do a bottom nav. - // The nav will pad the bottom inset, and an ancestor of this widget - // will have a `MediaQuery.removePadding` with `removeBottom: true`. - bottom: false, - - child: Center( - child: ConstrainedBox( - constraints: const BoxConstraints(maxWidth: 760), - child: NotificationListener( - onNotification: _handleScrollMetricsNotification, - child: Stack( - children: [ - _buildListView(context), - Positioned( - bottom: 0, - right: 0, - // TODO(#311) SafeArea shouldn't be needed if we have a - // bottom nav. That will pad the bottom inset. - child: SafeArea( - child: ScrollToBottomButton( - scrollController: scrollController, - visibleValue: _scrollToBottomVisibleValue))), - ])))))); + style: const TextStyle(color: Color.fromRGBO(0, 0, 0, 1)), + // Pad the left and right insets, for small devices in landscape. + child: SafeArea( + // Don't let this be the place we pad the bottom inset. When there's + // no compose box, we want to let the message-list content pad it. + // TODO(#311) Remove as unnecessary if we do a bottom nav. + // The nav will pad the bottom inset, and an ancestor of this widget + // will have a `MediaQuery.removePadding` with `removeBottom: true`. + bottom: false, + + child: Center( + child: ConstrainedBox( + constraints: const BoxConstraints(maxWidth: 760), + child: NotificationListener( + onNotification: _handleScrollMetricsNotification, + child: Stack( + children: [ + _buildListView(context), + Positioned( + bottom: 0, + right: 0, + // TODO(#311) SafeArea shouldn't be needed if we have a + // bottom nav. That will pad the bottom inset. + child: SafeArea( + child: ScrollToBottomButton( + scrollController: scrollController, + visibleValue: _scrollToBottomVisibleValue))), + ])))))); } Widget _buildListView(context) { @@ -306,91 +306,91 @@ class _MessageListState extends State with PerAccountStoreAwareStat // TODO: Offer `ScrollViewKeyboardDismissBehavior.interactive` (or // similar) if that is ever offered: // https://github.com/flutter/flutter/issues/57609#issuecomment-1355340849 - keyboardDismissBehavior: switch (Theme.of(context).platform) { + keyboardDismissBehavior: switch (Theme.of(context).platform) { // This seems to offer the only built-in way to close the keyboard // on iOS. It's not ideal; see TODO above. - TargetPlatform.iOS => ScrollViewKeyboardDismissBehavior.onDrag, + TargetPlatform.iOS => ScrollViewKeyboardDismissBehavior.onDrag, // The Android keyboard seems to have a built-in close button. - _ => ScrollViewKeyboardDismissBehavior.manual, - }, - - controller: scrollController, - semanticChildCount: length + 2, - anchor: 1.0, - center: centerSliverKey, - - slivers: [ - SliverStickyHeaderList( - headerPlacement: HeaderPlacement.scrollingStart, - delegate: SliverChildBuilderDelegate( - // To preserve state across rebuilds for individual [MessageItem] - // widgets as the size of [MessageListView.items] changes we need - // to match old widgets by their key to their new position in - // the list. - // - // The keys are of type [ValueKey] with a value of [Message.id] - // and here we use a O(log n) binary search method. This could - // be improved but for now it only triggers for materialized - // widgets. As a simple test, flinging through All Messages in - // CZO on a Pixel 5, this only runs about 10 times per rebuild - // and the timing for each call is <100 microseconds. - // - // Non-message items (e.g., start and end markers) that do not - // have state that needs to be preserved have not been given keys - // and will not trigger this callback. - findChildIndexCallback: (Key key) { - final valueKey = key as ValueKey; - final index = model!.findItemWithMessageId(valueKey.value); - if (index == -1) return null; - return length - 1 - (index - 2); - }, - childCount: length + 2, - (context, i) { - // To reinforce that the end of the feed has been reached: - // https://chat.zulip.org/#narrow/stream/243-mobile-team/topic/flutter.3A.20Mark-as-read/near/1680603 - if (i == 0) return const SizedBox(height: 36); - - if (i == 1) return MarkAsReadWidget(narrow: widget.narrow); - - final data = model!.items[length - 1 - (i - 2)]; - return _buildItem(data, i); - })), - - // This is a trivial placeholder that occupies no space. Its purpose is - // to have the key that's passed to [ScrollView.center], and so to cause - // the above [SliverStickyHeaderList] to run from bottom to top. - const SliverToBoxAdapter(key: centerSliverKey), - ]); + _ => ScrollViewKeyboardDismissBehavior.manual, + }, + + controller: scrollController, + semanticChildCount: length + 2, + anchor: 1.0, + center: centerSliverKey, + + slivers: [ + SliverStickyHeaderList( + headerPlacement: HeaderPlacement.scrollingStart, + delegate: SliverChildBuilderDelegate( + // To preserve state across rebuilds for individual [MessageItem] + // widgets as the size of [MessageListView.items] changes we need + // to match old widgets by their key to their new position in + // the list. + // + // The keys are of type [ValueKey] with a value of [Message.id] + // and here we use a O(log n) binary search method. This could + // be improved but for now it only triggers for materialized + // widgets. As a simple test, flinging through All Messages in + // CZO on a Pixel 5, this only runs about 10 times per rebuild + // and the timing for each call is <100 microseconds. + // + // Non-message items (e.g., start and end markers) that do not + // have state that needs to be preserved have not been given keys + // and will not trigger this callback. + findChildIndexCallback: (Key key) { + final valueKey = key as ValueKey; + final index = model!.findItemWithMessageId(valueKey.value); + if (index == -1) return null; + return length - 1 - (index - 2); + }, + childCount: length + 2, + (context, i) { + // To reinforce that the end of the feed has been reached: + // https://chat.zulip.org/#narrow/stream/243-mobile-team/topic/flutter.3A.20Mark-as-read/near/1680603 + if (i == 0) return const SizedBox(height: 36); + + if (i == 1) return MarkAsReadWidget(narrow: widget.narrow); + + final data = model!.items[length - 1 - (i - 2)]; + return _buildItem(data, i); + })), + + // This is a trivial placeholder that occupies no space. Its purpose is + // to have the key that's passed to [ScrollView.center], and so to cause + // the above [SliverStickyHeaderList] to run from bottom to top. + const SliverToBoxAdapter(key: centerSliverKey), + ]); } Widget _buildItem(MessageListItem data, int i) { switch (data) { case MessageListHistoryStartItem(): return const Center( - child: Padding( - padding: EdgeInsets.symmetric(vertical: 16.0), - child: Text("No earlier messages."))); // TODO use an icon + child: Padding( + padding: EdgeInsets.symmetric(vertical: 16.0), + child: Text("No earlier messages."))); // TODO use an icon case MessageListLoadingItem(): return const Center( - child: Padding( - padding: EdgeInsets.symmetric(vertical: 16.0), - child: CircularProgressIndicator())); // TODO perhaps a different indicator + child: Padding( + padding: EdgeInsets.symmetric(vertical: 16.0), + child: CircularProgressIndicator())); // TODO perhaps a different indicator case MessageListRecipientHeaderItem(): final header = RecipientHeader(message: data.message, narrow: widget.narrow); return StickyHeaderItem(allowOverflow: true, - header: header, child: header); + header: header, child: header); case MessageListDateSeparatorItem(): final header = RecipientHeader(message: data.message, narrow: widget.narrow); return StickyHeaderItem(allowOverflow: true, - header: header, - child: DateSeparator(message: data.message)); + header: header, + child: DateSeparator(message: data.message)); case MessageListMessageItem(): final header = RecipientHeader(message: data.message, narrow: widget.narrow); return MessageItem( - key: ValueKey(data.message.id), - header: header, - trailingWhitespace: i == 1 ? 8 : 11, - item: data); + key: ValueKey(data.message.id), + header: header, + trailingWhitespace: i == 1 ? 8 : 11, + item: data); } } } @@ -406,25 +406,25 @@ class ScrollToBottomButton extends StatelessWidget { final durationMsAtSpeedLimit = (1000 * distance / 8000).ceil(); final durationMs = max(300, durationMsAtSpeedLimit); scrollController.animateTo( - 0, - duration: Duration(milliseconds: durationMs), - curve: Curves.ease); + 0, + duration: Duration(milliseconds: durationMs), + curve: Curves.ease); } @override Widget build(BuildContext context) { return ValueListenableBuilder( - valueListenable: visibleValue, - builder: (BuildContext context, bool value, Widget? child) { - return (value && child != null) ? child : const SizedBox.shrink(); - }, - // TODO: fix hardcoded values for size and style here - child: IconButton( - tooltip: "Scroll to bottom", - icon: const Icon(Icons.expand_circle_down_rounded), - iconSize: 40, - color: const HSLColor.fromAHSL(0.5,240,0.96,0.68).toColor(), - onPressed: _navigateToBottom)); + valueListenable: visibleValue, + builder: (BuildContext context, bool value, Widget? child) { + return (value && child != null) ? child : const SizedBox.shrink(); + }, + // TODO: fix hardcoded values for size and style here + child: IconButton( + tooltip: "Scroll to bottom", + icon: const Icon(Icons.expand_circle_down_rounded), + iconSize: 40, + color: const HSLColor.fromAHSL(0.5,240,0.96,0.68).toColor(), + onPressed: _navigateToBottom)); } } @@ -446,8 +446,8 @@ class MarkAsReadWidget extends StatelessWidget { if (!context.mounted) return; final zulipLocalizations = ZulipLocalizations.of(context); await showErrorDialog(context: context, - title: zulipLocalizations.errorMarkAsReadFailedTitle, - message: e.toString()); + title: zulipLocalizations.errorMarkAsReadFailedTitle, + message: e.toString()); return; } if (!context.mounted) return; @@ -466,33 +466,33 @@ class MarkAsReadWidget extends StatelessWidget { return IgnorePointer( ignoring: areMessagesRead, child: AnimatedOpacity( - opacity: areMessagesRead ? 0 : 1, - duration: Duration(milliseconds: areMessagesRead ? 2000 : 300), - curve: Curves.easeOut, - child: SizedBox(width: double.infinity, - // Design referenced from: - // https://www.figma.com/file/1JTNtYo9memgW7vV6d0ygq/Zulip-Mobile?type=design&node-id=132-9684&mode=design&t=jJwHzloKJ0TMOG4M-0 - child: Padding( - // vertical padding adjusted for tap target height (48px) of button - padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 10 - ((48 - 38) / 2)), - child: FilledButton.icon( - style: FilledButton.styleFrom( - backgroundColor: _UnreadMarker.color, - minimumSize: const Size.fromHeight(38), - textStyle: - // Restate [FilledButton]'s default, which inherits from - // [zulipTypography]… - Theme.of(context).textTheme.labelLarge! - // …then clobber some attributes to follow Figma: - .merge(const TextStyle( - fontSize: 18, - height: (23 / 18)) - .merge(weightVariableTextStyle(context, wght: 400))), - shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(7)), - ), - onPressed: () => _handlePress(context), - icon: const Icon(Icons.playlist_add_check), - label: Text(zulipLocalizations.markAllAsReadLabel))))), + opacity: areMessagesRead ? 0 : 1, + duration: Duration(milliseconds: areMessagesRead ? 2000 : 300), + curve: Curves.easeOut, + child: SizedBox(width: double.infinity, + // Design referenced from: + // https://www.figma.com/file/1JTNtYo9memgW7vV6d0ygq/Zulip-Mobile?type=design&node-id=132-9684&mode=design&t=jJwHzloKJ0TMOG4M-0 + child: Padding( + // vertical padding adjusted for tap target height (48px) of button + padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 10 - ((48 - 38) / 2)), + child: FilledButton.icon( + style: FilledButton.styleFrom( + backgroundColor: _UnreadMarker.color, + minimumSize: const Size.fromHeight(38), + textStyle: + // Restate [FilledButton]'s default, which inherits from + // [zulipTypography]… + Theme.of(context).textTheme.labelLarge! + // …then clobber some attributes to follow Figma: + .merge(const TextStyle( + fontSize: 18, + height: (23 / 18)) + .merge(weightVariableTextStyle(context, wght: 400))), + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(7)), + ), + onPressed: () => _handlePress(context), + icon: const Icon(Icons.playlist_add_check), + label: Text(zulipLocalizations.markAllAsReadLabel))))), ); } } @@ -508,7 +508,7 @@ class RecipientHeader extends StatelessWidget { final message = this.message; return switch (message) { StreamMessage() => StreamMessageRecipientHeader(message: message, - showStream: narrow is AllMessagesNarrow), + showStream: narrow is AllMessagesNarrow), DmMessage() => DmRecipientHeader(message: message), }; } @@ -530,30 +530,30 @@ class DateSeparator extends StatelessWidget { return ColoredBox(color: Colors.white, child: Padding( - padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 2), - child: Row(children: [ - const Expanded( - child: SizedBox(height: 0, - child: DecoratedBox( - decoration: BoxDecoration( - border: Border( - bottom: BorderSide( - width: 0, - color: Colors.black)))))), - Padding(padding: const EdgeInsets.fromLTRB(2, 0, 2, textBottomPadding), - child: DateText( - color: _textColor, - fontSize: 16, - height: (16 / 16), - timestamp: message.timestamp)), - const SizedBox(height: 0, width: 12, - child: DecoratedBox( - decoration: BoxDecoration( - border: Border( - bottom: BorderSide( - width: 0, - color: Colors.black))))) - ])), + padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 2), + child: Row(children: [ + const Expanded( + child: SizedBox(height: 0, + child: DecoratedBox( + decoration: BoxDecoration( + border: Border( + bottom: BorderSide( + width: 0, + color: Colors.black)))))), + Padding(padding: const EdgeInsets.fromLTRB(2, 0, 2, textBottomPadding), + child: DateText( + color: _textColor, + fontSize: 16, + height: (16 / 16), + timestamp: message.timestamp)), + const SizedBox(height: 0, width: 12, + child: DecoratedBox( + decoration: BoxDecoration( + border: Border( + bottom: BorderSide( + width: 0, + color: Colors.black))))) + ])), ); } } @@ -574,16 +574,16 @@ class MessageItem extends StatelessWidget { Widget build(BuildContext context) { final message = item.message; return StickyHeaderItem( - allowOverflow: !item.isLastInBlock, - header: header, - child: _UnreadMarker( - isRead: message.flags.contains(MessageFlag.read), - child: ColoredBox( - color: Colors.white, - child: Column(children: [ - MessageWithPossibleSender(item: item), - if (trailingWhitespace != null && item.isLastInBlock) SizedBox(height: trailingWhitespace!), - ])))); + allowOverflow: !item.isLastInBlock, + header: header, + child: _UnreadMarker( + isRead: message.flags.contains(MessageFlag.read), + child: ColoredBox( + color: Colors.white, + child: Column(children: [ + MessageWithPossibleSender(item: item), + if (trailingWhitespace != null && item.isLastInBlock) SizedBox(height: trailingWhitespace!), + ])))); } } @@ -603,28 +603,28 @@ class _UnreadMarker extends StatelessWidget { @override Widget build(BuildContext context) { return Stack( - children: [ - child, - Positioned( - top: 0, - left: 0, - bottom: 0, - width: 4, - child: AnimatedOpacity( - opacity: isRead ? 0 : 1, - // Web uses 2s and 0.3s durations, and a CSS ease-out curve. - // See zulip:web/styles/message_row.css . - duration: Duration(milliseconds: isRead ? 2000 : 300), - curve: Curves.easeOut, - child: DecoratedBox( - decoration: BoxDecoration( - color: color, - // TODO(#95): Don't show this extra border in dark mode, see: - // https://github.com/zulip/zulip-flutter/pull/317#issuecomment-1784311663 - border: Border(left: BorderSide( - width: 1, - color: Colors.white.withOpacity(0.6))))))), - ]); + children: [ + child, + Positioned( + top: 0, + left: 0, + bottom: 0, + width: 4, + child: AnimatedOpacity( + opacity: isRead ? 0 : 1, + // Web uses 2s and 0.3s durations, and a CSS ease-out curve. + // See zulip:web/styles/message_row.css . + duration: Duration(milliseconds: isRead ? 2000 : 300), + curve: Curves.easeOut, + child: DecoratedBox( + decoration: BoxDecoration( + color: color, + // TODO(#95): Don't show this extra border in dark mode, see: + // https://github.com/zulip/zulip-flutter/pull/317#issuecomment-1784311663 + border: Border(left: BorderSide( + width: 1, + color: Colors.white.withOpacity(0.6))))))), + ]); } } @@ -655,7 +655,7 @@ class StreamMessageRecipientHeader extends StatelessWidget { final swatch = subscription.colorSwatch(); backgroundColor = swatch.barBackground; contrastingColor = - (ThemeData.estimateBrightnessForColor(swatch.barBackground) == Brightness.dark) + (ThemeData.estimateBrightnessForColor(swatch.barBackground) == Brightness.dark) ? Colors.white : Colors.black; iconColor = swatch.iconOnBarBackground; @@ -679,62 +679,62 @@ class StreamMessageRecipientHeader extends StatelessWidget { final streamName = stream?.name ?? message.displayRecipient; // TODO(log) if missing streamWidget = GestureDetector( - onTap: () => Navigator.push(context, - MessageListPage.buildRoute(context: context, - narrow: StreamNarrow(message.streamId))), - child: Row( - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Padding( - // Figma specifies 5px horizontal spacing around an icon that's - // 18x18 and includes 1px padding. The icon SVG is flush with - // the edges, so make it 16x16 with 6px horizontal padding. - // Bottom padding added here to shift icon up to - // match alignment with text visually. - padding: const EdgeInsets.only(left: 6, right: 6, bottom: 3), - child: Icon(size: 16, color: iconColor, - // A null [Icon.icon] makes a blank space. - (stream != null) ? iconDataForStream(stream) : null)), - Padding( - padding: const EdgeInsets.symmetric(vertical: 11), - child: Text(streamName, - style: textStyle, - overflow: TextOverflow.ellipsis), - ), - Padding( - // Figma has 5px horizontal padding around an 8px wide icon. - // Icon is 16px wide here so horizontal padding is 1px. - padding: const EdgeInsets.symmetric(horizontal: 1), - child: Icon(size: 16, - color: contrastingColor.withOpacity(0.6), - ZulipIcons.chevron_right)), - ])); + onTap: () => Navigator.push(context, + MessageListPage.buildRoute(context: context, + narrow: StreamNarrow(message.streamId))), + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Padding( + // Figma specifies 5px horizontal spacing around an icon that's + // 18x18 and includes 1px padding. The icon SVG is flush with + // the edges, so make it 16x16 with 6px horizontal padding. + // Bottom padding added here to shift icon up to + // match alignment with text visually. + padding: const EdgeInsets.only(left: 6, right: 6, bottom: 3), + child: Icon(size: 16, color: iconColor, + // A null [Icon.icon] makes a blank space. + (stream != null) ? iconDataForStream(stream) : null)), + Padding( + padding: const EdgeInsets.symmetric(vertical: 11), + child: Text(streamName, + style: textStyle, + overflow: TextOverflow.ellipsis), + ), + Padding( + // Figma has 5px horizontal padding around an 8px wide icon. + // Icon is 16px wide here so horizontal padding is 1px. + padding: const EdgeInsets.symmetric(horizontal: 1), + child: Icon(size: 16, + color: contrastingColor.withOpacity(0.6), + ZulipIcons.chevron_right)), + ])); } return GestureDetector( - onTap: () => Navigator.push(context, - MessageListPage.buildRoute(context: context, - narrow: TopicNarrow.ofMessage(message))), - child: ColoredBox( - color: backgroundColor, - child: Row( - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - // TODO(#282): Long stream name will break layout; find a fix. - streamWidget, - Expanded( - child: Padding( - padding: const EdgeInsets.symmetric(vertical: 11), - child: Text(topic, - // TODO: Give a way to see the whole topic (maybe a - // long-press interaction?) - overflow: TextOverflow.ellipsis, - style: textStyle))), - // TODO topic links? - // Then web also has edit/resolve/mute buttons. Skip those for mobile. - RecipientHeaderDate(message: message, - color: contrastingColor.withOpacity(0.4)), - ]))); + onTap: () => Navigator.push(context, + MessageListPage.buildRoute(context: context, + narrow: TopicNarrow.ofMessage(message))), + child: ColoredBox( + color: backgroundColor, + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + // TODO(#282): Long stream name will break layout; find a fix. + streamWidget, + Expanded( + child: Padding( + padding: const EdgeInsets.symmetric(vertical: 11), + child: Text(topic, + // TODO: Give a way to see the whole topic (maybe a + // long-press interaction?) + overflow: TextOverflow.ellipsis, + style: textStyle))), + // TODO topic links? + // Then web also has edit/resolve/mute buttons. Skip those for mobile. + RecipientHeaderDate(message: message, + color: contrastingColor.withOpacity(0.4)), + ]))); } } @@ -750,40 +750,40 @@ class DmRecipientHeader extends StatelessWidget { final String title; if (message.allRecipientIds.length > 1) { title = zulipLocalizations.messageListGroupYouAndOthers(message.allRecipientIds - .where((id) => id != store.selfUserId) - .map((id) => store.users[id]?.fullName ?? zulipLocalizations.unknownUserName) - .sorted() - .join(", ")); + .where((id) => id != store.selfUserId) + .map((id) => store.users[id]?.fullName ?? zulipLocalizations.unknownUserName) + .sorted() + .join(", ")); } else { // TODO pick string; web has glitchy "You and $yourname" title = zulipLocalizations.messageListGroupYouWithYourself; } return GestureDetector( - onTap: () => Navigator.push(context, - MessageListPage.buildRoute(context: context, - narrow: DmNarrow.ofMessage(message, selfUserId: store.selfUserId))), - child: ColoredBox( - color: _kDmRecipientHeaderColor, - child: Padding( - padding: const EdgeInsets.symmetric(vertical: 11), - child: Row( - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - const Padding( - padding: EdgeInsets.symmetric(horizontal: 6), - child: Icon(size: 16, ZulipIcons.user)), - Expanded( - child: Text(title, - style: const TextStyle( - fontSize: 16, - letterSpacing: 0.02 * 16, - height: (18 / 16), - ).merge(weightVariableTextStyle(context, wght: 600)), - overflow: TextOverflow.ellipsis)), - RecipientHeaderDate(message: message, - color: _kDmRecipientHeaderDateColor), - ])))); + onTap: () => Navigator.push(context, + MessageListPage.buildRoute(context: context, + narrow: DmNarrow.ofMessage(message, selfUserId: store.selfUserId))), + child: ColoredBox( + color: _kDmRecipientHeaderColor, + child: Padding( + padding: const EdgeInsets.symmetric(vertical: 11), + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + const Padding( + padding: EdgeInsets.symmetric(horizontal: 6), + child: Icon(size: 16, ZulipIcons.user)), + Expanded( + child: Text(title, + style: const TextStyle( + fontSize: 16, + letterSpacing: 0.02 * 16, + height: (18 / 16), + ).merge(weightVariableTextStyle(context, wght: 600)), + overflow: TextOverflow.ellipsis)), + RecipientHeaderDate(message: message, + color: _kDmRecipientHeaderDateColor), + ])))); } } @@ -802,15 +802,15 @@ class RecipientHeaderDate extends StatelessWidget { @override Widget build(BuildContext context) { return Padding( - padding: const EdgeInsets.fromLTRB(10, 0, 16, 0), - child: DateText( - color: color, - fontSize: 16, - // In Figma this has a line-height of 19, but using 18 - // here to match the stream/topic text widgets helps - // to align all the text to the same baseline. - height: (18 / 16), - timestamp: message.timestamp)); + padding: const EdgeInsets.fromLTRB(10, 0, 16, 0), + child: DateText( + color: color, + fontSize: 16, + // In Figma this has a line-height of 19, but using 18 + // here to match the stream/topic text widgets helps + // to align all the text to the same baseline. + height: (18 / 16), + timestamp: message.timestamp)); } } @@ -832,29 +832,29 @@ class DateText extends StatelessWidget { Widget build(BuildContext context) { final zulipLocalizations = ZulipLocalizations.of(context); return Text( - style: TextStyle( - color: color, - fontSize: fontSize, - height: height, - // This is equivalent to css `all-small-caps`, see: - // https://developer.mozilla.org/en-US/docs/Web/CSS/font-variant-caps#all-small-caps - fontFeatures: const [FontFeature.enable('c2sc'), FontFeature.enable('smcp')], - ), - formatHeaderDate( - zulipLocalizations, - DateTime.fromMillisecondsSinceEpoch(timestamp * 1000), - now: DateTime.now())); + style: TextStyle( + color: color, + fontSize: fontSize, + height: height, + // This is equivalent to css `all-small-caps`, see: + // https://developer.mozilla.org/en-US/docs/Web/CSS/font-variant-caps#all-small-caps + fontFeatures: const [FontFeature.enable('c2sc'), FontFeature.enable('smcp')], + ), + formatHeaderDate( + zulipLocalizations, + DateTime.fromMillisecondsSinceEpoch(timestamp * 1000), + now: DateTime.now())); } } @visibleForTesting String formatHeaderDate( - ZulipLocalizations zulipLocalizations, - DateTime dateTime, { - required DateTime now, -}) { + ZulipLocalizations zulipLocalizations, + DateTime dateTime, { + required DateTime now, + }) { assert(!dateTime.isUtc && !now.isUtc, - '`dateTime` and `now` need to be in local time.'); + '`dateTime` and `now` need to be in local time.'); if (dateTime.year == now.year && dateTime.month == now.month && @@ -863,8 +863,8 @@ String formatHeaderDate( } final yesterday = now - .copyWith(hour: 12, minute: 0, second: 0, millisecond: 0, microsecond: 0) - .add(const Duration(days: -1)); + .copyWith(hour: 12, minute: 0, second: 0, millisecond: 0, microsecond: 0) + .add(const Duration(days: -1)); if (dateTime.year == yesterday.year && dateTime.month == yesterday.month && dateTime.day == yesterday.day) { @@ -882,13 +882,12 @@ String formatHeaderDate( return DateFormat.yMMMd().format(dateTime); } } - /// A Zulip message, showing the sender's name and avatar if specified. // Design referenced from: // - https://github.com/zulip/zulip-mobile/issues/5511 // - https://www.figma.com/file/1JTNtYo9memgW7vV6d0ygq/Zulip-Mobile?node-id=538%3A20849&mode=dev class MessageWithPossibleSender extends StatelessWidget { - const MessageWithPossibleSender({super.key, required this.item}); + const MessageWithPossibleSender({Key? key, required this.item}) : super(key: key); final MessageListMessageItem item; @@ -904,7 +903,7 @@ class MessageWithPossibleSender extends StatelessWidget { Widget? senderRow; if (item.showSender) { final time = _kMessageTimestampFormat - .format(DateTime.fromMillisecondsSinceEpoch(1000 * message.timestamp)); + .format(DateTime.fromMillisecondsSinceEpoch(1000 * message.timestamp)); senderRow = Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, crossAxisAlignment: CrossAxisAlignment.baseline, @@ -912,42 +911,49 @@ class MessageWithPossibleSender extends StatelessWidget { children: [ Flexible( child: GestureDetector( - onTap: () => Navigator.push(context, - ProfilePage.buildRoute(context: context, - userId: message.senderId)), - child: Row( - children: [ - Avatar(size: 32, borderRadius: 3, - userId: message.senderId), - const SizedBox(width: 8), - Flexible( - child: Text(message.senderFullName, // TODO get from user data - style: const TextStyle( - fontFamily: 'Source Sans 3', - fontSize: 18, - height: (22 / 18), - ).merge(weightVariableTextStyle(context, wght: 600, - wghtIfPlatformRequestsBold: 900)), - overflow: TextOverflow.ellipsis)), - if (sender?.isBot ?? false) ...[ - const SizedBox(width: 5), - const Icon( - ZulipIcons.bot, - size: 15, - color: Color.fromARGB(255, 159, 173, 173), - ), + onTap: () => Navigator.push(context, + ProfilePage.buildRoute(context: context, userId: message.senderId)), + child: Row( + children: [ + Avatar( + size: 32, + borderRadius: 3, + userId: message.senderId), + const SizedBox(width: 8), + Flexible( + child: Text( + message.senderFullName, // TODO get from user data + style: const TextStyle( + fontFamily: 'Source Sans 3', + fontSize: 18, + height: (22 / 18), + ), + overflow: TextOverflow.ellipsis, + )), + if (sender?.isBot ?? false) ...[ + const SizedBox(width: 5), + const Icon( + ZulipIcons.bot, + size: 15, + color: Color.fromARGB(255, 159, 173, 173), + ), + ], ], - ]))), + )), + ), const SizedBox(width: 4), - Text(time, + Text( + time, style: TextStyle( color: _kMessageTimestampColor, fontFamily: 'Source Sans 3', fontSize: 16, height: (18 / 16), fontFeatures: const [FontFeature.enable('c2sc'), FontFeature.enable('smcp')], - ).merge(weightVariableTextStyle(context))), - ]); + ), + ), + ], + ); } return GestureDetector( @@ -955,30 +961,41 @@ class MessageWithPossibleSender extends StatelessWidget { onLongPress: () => showMessageActionSheet(context: context, message: message), child: Padding( padding: const EdgeInsets.symmetric(vertical: 4), - child: Column(children: [ - if (senderRow != null) - Padding(padding: const EdgeInsets.fromLTRB(16, 2, 16, 0), - child: senderRow), - Row(crossAxisAlignment: CrossAxisAlignment.start, children: [ - const SizedBox(width: 16), - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - MessageContent(message: message, content: item.content), - if ((message.reactions?.total ?? 0) > 0) - ReactionChipsList(messageId: message.id, reactions: message.reactions!) - ])), - SizedBox(width: 16, + child: Column( + children: [ + if (senderRow != null) + Padding( + padding: const EdgeInsets.fromLTRB(16, 2, 16, 0), + child: senderRow, + ), + Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const SizedBox(width: 16), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + MessageContent(message: message, content: item.content), + if ((message.reactions?.total ?? 0) > 0) + ReactionChipsList(messageId: message.id, reactions: message.reactions!) + ], + ), + ), + SizedBox(width: 17, child: message.flags.contains(MessageFlag.starred) // TODO(#157): fix how star marker aligns with message content // Design from Figma at: // https://www.figma.com/file/1JTNtYo9memgW7vV6d0ygq/Zulip-Mobile?node-id=813%3A28817&mode=dev . - ? Padding(padding: const EdgeInsets.only(top: 4), - child: Icon(ZulipIcons.star_filled, size: 16, color: _starColor)) - : null), - ]), - ]))); + ? Padding(padding: const EdgeInsets.only(top: 5.5), + child: Icon(ZulipIcons.star_filled, size: 17, color: _starColor)) + : null) + ], + ), + ], + ), + ), + ); } } @@ -988,10 +1005,10 @@ final _kMessageTimestampFormat = DateFormat('h:mm aa', 'en_US'); final _kMessageTimestampColor = const HSLColor.fromAHSL(1, 0, 0, 0.5).toColor(); Future markNarrowAsRead( - BuildContext context, - Narrow narrow, - bool useLegacy, // TODO(server-6) -) async { + BuildContext context, + Narrow narrow, + bool useLegacy, // TODO(server-6) + ) async { final store = PerAccountStoreWidget.of(context); final connection = store.connection; if (useLegacy) { @@ -1019,22 +1036,22 @@ Future markNarrowAsRead( while (true) { final result = await updateMessageFlagsForNarrow(connection, - anchor: anchor, - // [AnchorCode.oldest] is an anchor ID lower than any valid - // message ID; and follow-up requests will have already - // processed the anchor ID, so we just want this to be - // unconditionally false. - includeAnchor: false, - // There is an upper limit of 5000 messages per batch - // (numBefore + numAfter <= 5000) enforced on the server. - // See `update_message_flags_in_narrow` in zerver/views/message_flags.py . - // zulip-mobile uses `numAfter` of 5000, but web uses 1000 - // for more responsive feedback. See zulip@f0d87fcf6. - numBefore: 0, - numAfter: 1000, - narrow: apiNarrow, - op: UpdateMessageFlagsOp.add, - flag: MessageFlag.read); + anchor: anchor, + // [AnchorCode.oldest] is an anchor ID lower than any valid + // message ID; and follow-up requests will have already + // processed the anchor ID, so we just want this to be + // unconditionally false. + includeAnchor: false, + // There is an upper limit of 5000 messages per batch + // (numBefore + numAfter <= 5000) enforced on the server. + // See `update_message_flags_in_narrow` in zerver/views/message_flags.py . + // zulip-mobile uses `numAfter` of 5000, but web uses 1000 + // for more responsive feedback. See zulip@f0d87fcf6. + numBefore: 0, + numAfter: 1000, + narrow: apiNarrow, + op: UpdateMessageFlagsOp.add, + flag: MessageFlag.read); if (!context.mounted) { scaffoldMessenger.clearSnackBars(); return; @@ -1060,8 +1077,8 @@ Future markNarrowAsRead( // This should be impossible given that `foundNewest` was false // (and that our `numAfter` was positive.) await showErrorDialog(context: context, - title: zulipLocalizations.errorMarkAsReadFailedTitle, - message: zulipLocalizations.errorInvalidResponse); + title: zulipLocalizations.errorMarkAsReadFailedTitle, + message: zulipLocalizations.errorInvalidResponse); return; } anchor = NumericAnchor(result.lastProcessedId!); @@ -1081,7 +1098,7 @@ Future markNarrowAsRead( // is better for now if we allow them to run their timer through // and clear the backlog later. scaffoldMessenger.showSnackBar(SnackBar(behavior: SnackBarBehavior.floating, - content: Text(zulipLocalizations.markAsReadInProgress))); + content: Text(zulipLocalizations.markAsReadInProgress))); } } @@ -1102,8 +1119,8 @@ Future _legacyMarkNarrowAsRead(BuildContext context, Narrow narrow) async // of pushing the button. if (unreadDms == null) return; await updateMessageFlags(connection, - messages: unreadDms, - op: UpdateMessageFlagsOp.add, - flag: MessageFlag.read); + messages: unreadDms, + op: UpdateMessageFlagsOp.add, + flag: MessageFlag.read); } } diff --git a/pubspec.lock b/pubspec.lock deleted file mode 100644 index 18509fd60a..0000000000 --- a/pubspec.lock +++ /dev/null @@ -1,1301 +0,0 @@ -# Generated by pub -# See https://dart.dev/tools/pub/glossary#lockfile -packages: - _fe_analyzer_shared: - dependency: transitive - description: - name: _fe_analyzer_shared - sha256: "0b2f2bd91ba804e53a61d757b986f89f1f9eaed5b11e4b2f5a2468d86d6c9fc7" - url: "https://pub.dev" - source: hosted - version: "67.0.0" - _flutterfire_internals: - dependency: transitive - description: - name: _flutterfire_internals - sha256: "79b6452b4066fcbdd74c2aac354e80c591a727e0364bedccecdb5a5321784fa2" - url: "https://pub.dev" - source: hosted - version: "1.3.28" - analyzer: - dependency: transitive - description: - name: analyzer - sha256: "37577842a27e4338429a1cbc32679d508836510b056f1eedf0c8d20e39c1383d" - url: "https://pub.dev" - source: hosted - version: "6.4.1" - analyzer_plugin: - dependency: transitive - description: - name: analyzer_plugin - sha256: "9661b30b13a685efaee9f02e5d01ed9f2b423bd889d28a304d02d704aee69161" - url: "https://pub.dev" - source: hosted - version: "0.11.3" - app_settings: - dependency: "direct main" - description: - name: app_settings - sha256: "09bc7fe0313a507087bec1a3baf555f0576e816a760cbb31813a88890a09d9e5" - url: "https://pub.dev" - source: hosted - version: "5.1.1" - args: - dependency: transitive - description: - name: args - sha256: eef6c46b622e0494a36c5a12d10d77fb4e855501a91c1b9ef9339326e58f0596 - url: "https://pub.dev" - source: hosted - version: "2.4.2" - async: - dependency: transitive - description: - name: async - sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" - url: "https://pub.dev" - source: hosted - version: "2.11.0" - boolean_selector: - dependency: transitive - description: - name: boolean_selector - sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" - url: "https://pub.dev" - source: hosted - version: "2.1.1" - build: - dependency: transitive - description: - name: build - sha256: "80184af8b6cb3e5c1c4ec6d8544d27711700bc3e6d2efad04238c7b5290889f0" - url: "https://pub.dev" - source: hosted - version: "2.4.1" - build_config: - dependency: transitive - description: - name: build_config - sha256: bf80fcfb46a29945b423bd9aad884590fb1dc69b330a4d4700cac476af1708d1 - url: "https://pub.dev" - source: hosted - version: "1.1.1" - build_daemon: - dependency: transitive - description: - name: build_daemon - sha256: "0343061a33da9c5810b2d6cee51945127d8f4c060b7fbdd9d54917f0a3feaaa1" - url: "https://pub.dev" - source: hosted - version: "4.0.1" - build_resolvers: - dependency: transitive - description: - name: build_resolvers - sha256: "339086358431fa15d7eca8b6a36e5d783728cf025e559b834f4609a1fcfb7b0a" - url: "https://pub.dev" - source: hosted - version: "2.4.2" - build_runner: - dependency: "direct dev" - description: - name: build_runner - sha256: "3ac61a79bfb6f6cc11f693591063a7f19a7af628dc52f141743edac5c16e8c22" - url: "https://pub.dev" - source: hosted - version: "2.4.9" - build_runner_core: - dependency: transitive - description: - name: build_runner_core - sha256: "4ae8ffe5ac758da294ecf1802f2aff01558d8b1b00616aa7538ea9a8a5d50799" - url: "https://pub.dev" - source: hosted - version: "7.3.0" - built_collection: - dependency: transitive - description: - name: built_collection - sha256: "376e3dd27b51ea877c28d525560790aee2e6fbb5f20e2f85d5081027d94e2100" - url: "https://pub.dev" - source: hosted - version: "5.1.1" - built_value: - dependency: transitive - description: - name: built_value - sha256: c7913a9737ee4007efedaffc968c049fd0f3d0e49109e778edc10de9426005cb - url: "https://pub.dev" - source: hosted - version: "8.9.2" - characters: - dependency: transitive - description: - name: characters - sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" - url: "https://pub.dev" - source: hosted - version: "1.3.0" - charcode: - dependency: transitive - description: - name: charcode - sha256: fb98c0f6d12c920a02ee2d998da788bca066ca5f148492b7085ee23372b12306 - url: "https://pub.dev" - source: hosted - version: "1.3.1" - checked_yaml: - dependency: transitive - description: - name: checked_yaml - sha256: feb6bed21949061731a7a75fc5d2aa727cf160b91af9a3e464c5e3a32e28b5ff - url: "https://pub.dev" - source: hosted - version: "2.0.3" - checks: - dependency: "direct dev" - description: - name: checks - sha256: aad431b45a8ae2fa26db8c22e385b9cdec73f72986a1d9d9f2017f4c39ecf5c9 - url: "https://pub.dev" - source: hosted - version: "0.3.0" - cli_util: - dependency: transitive - description: - name: cli_util - sha256: c05b7406fdabc7a49a3929d4af76bcaccbbffcbcdcf185b082e1ae07da323d19 - url: "https://pub.dev" - source: hosted - version: "0.4.1" - clock: - dependency: "direct dev" - description: - name: clock - sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf - url: "https://pub.dev" - source: hosted - version: "1.1.1" - code_builder: - dependency: transitive - description: - name: code_builder - sha256: f692079e25e7869c14132d39f223f8eec9830eb76131925143b2129c4bb01b37 - url: "https://pub.dev" - source: hosted - version: "4.10.0" - collection: - dependency: "direct main" - description: - name: collection - sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a - url: "https://pub.dev" - source: hosted - version: "1.18.0" - color_models: - dependency: transitive - description: - name: color_models - sha256: "3683f0a461570161ca22b7d3e1765b2ce2bce3534db14e00d5ee458d0287abac" - url: "https://pub.dev" - source: hosted - version: "1.3.3" - convert: - dependency: "direct main" - description: - name: convert - sha256: "0f08b14755d163f6e2134cb58222dd25ea2a2ee8a195e53983d57c075324d592" - url: "https://pub.dev" - source: hosted - version: "3.1.1" - coverage: - dependency: transitive - description: - name: coverage - sha256: "8acabb8306b57a409bf4c83522065672ee13179297a6bb0cb9ead73948df7c76" - url: "https://pub.dev" - source: hosted - version: "1.7.2" - cross_file: - dependency: transitive - description: - name: cross_file - sha256: "55d7b444feb71301ef6b8838dbc1ae02e63dd48c8773f3810ff53bb1e2945b32" - url: "https://pub.dev" - source: hosted - version: "0.3.4+1" - crypto: - dependency: "direct main" - description: - name: crypto - sha256: ff625774173754681d66daaf4a448684fb04b78f902da9cb3d308c19cc5e8bab - url: "https://pub.dev" - source: hosted - version: "3.0.3" - csslib: - dependency: transitive - description: - name: csslib - sha256: "706b5707578e0c1b4b7550f64078f0a0f19dec3f50a178ffae7006b0a9ca58fb" - url: "https://pub.dev" - source: hosted - version: "1.0.0" - dart_style: - dependency: transitive - description: - name: dart_style - sha256: "99e066ce75c89d6b29903d788a7bb9369cf754f7b24bf70bf4b6d6d6b26853b9" - url: "https://pub.dev" - source: hosted - version: "2.3.6" - dbus: - dependency: transitive - description: - name: dbus - sha256: "365c771ac3b0e58845f39ec6deebc76e3276aa9922b0cc60840712094d9047ac" - url: "https://pub.dev" - source: hosted - version: "0.7.10" - device_info_plus: - dependency: "direct main" - description: - name: device_info_plus - sha256: "50fb435ed30c6d2525cbfaaa0f46851ea6131315f213c0d921b0e407b34e3b84" - url: "https://pub.dev" - source: hosted - version: "10.0.1" - device_info_plus_platform_interface: - dependency: transitive - description: - name: device_info_plus_platform_interface - sha256: d3b01d5868b50ae571cd1dc6e502fc94d956b665756180f7b16ead09e836fd64 - url: "https://pub.dev" - source: hosted - version: "7.0.0" - drift: - dependency: "direct main" - description: - name: drift - sha256: "3b276c838ff7f8e19aac18a51f9b388715268f3534eaaf8047c8455ef3c1738d" - url: "https://pub.dev" - source: hosted - version: "2.16.0" - drift_dev: - dependency: "direct dev" - description: - name: drift_dev - sha256: "66cf3e397448f855523d7b6b7b3789db232b211db96543a42285464d05f3bf72" - url: "https://pub.dev" - source: hosted - version: "2.16.0" - fake_async: - dependency: "direct dev" - description: - name: fake_async - sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" - url: "https://pub.dev" - source: hosted - version: "1.3.1" - ffi: - dependency: transitive - description: - name: ffi - sha256: "493f37e7df1804778ff3a53bd691d8692ddf69702cf4c1c1096a2e41b4779e21" - url: "https://pub.dev" - source: hosted - version: "2.1.2" - file: - dependency: transitive - description: - name: file - sha256: "5fc22d7c25582e38ad9a8515372cd9a93834027aacf1801cf01164dac0ffa08c" - url: "https://pub.dev" - source: hosted - version: "7.0.0" - file_picker: - dependency: "direct main" - description: - name: file_picker - sha256: d1d0ac3966b36dc3e66eeefb40280c17feb87fa2099c6e22e6a1fc959327bd03 - url: "https://pub.dev" - source: hosted - version: "8.0.0+1" - file_selector_linux: - dependency: transitive - description: - name: file_selector_linux - sha256: "045d372bf19b02aeb69cacf8b4009555fb5f6f0b7ad8016e5f46dd1387ddd492" - url: "https://pub.dev" - source: hosted - version: "0.9.2+1" - file_selector_macos: - dependency: transitive - description: - name: file_selector_macos - sha256: b15c3da8bd4908b9918111fa486903f5808e388b8d1c559949f584725a6594d6 - url: "https://pub.dev" - source: hosted - version: "0.9.3+3" - file_selector_platform_interface: - dependency: transitive - description: - name: file_selector_platform_interface - sha256: a3994c26f10378a039faa11de174d7b78eb8f79e4dd0af2a451410c1a5c3f66b - url: "https://pub.dev" - source: hosted - version: "2.6.2" - file_selector_windows: - dependency: transitive - description: - name: file_selector_windows - sha256: d3547240c20cabf205c7c7f01a50ecdbc413755814d6677f3cb366f04abcead0 - url: "https://pub.dev" - source: hosted - version: "0.9.3+1" - firebase_core: - dependency: "direct main" - description: - name: firebase_core - sha256: "4b45655ec1b21a1783681f72f840a2e74d298046c2b7c286ab0e4f0efbf93d0a" - url: "https://pub.dev" - source: hosted - version: "2.28.0" - firebase_core_platform_interface: - dependency: transitive - description: - name: firebase_core_platform_interface - sha256: c437ae5d17e6b5cc7981cf6fd458a5db4d12979905f9aafd1fea930428a9fe63 - url: "https://pub.dev" - source: hosted - version: "5.0.0" - firebase_core_web: - dependency: transitive - description: - name: firebase_core_web - sha256: "28e30e00748497b9a70db2025942a42c5d752534eb678e9b9b98db056cf404ba" - url: "https://pub.dev" - source: hosted - version: "2.14.0" - firebase_messaging: - dependency: "direct main" - description: - name: firebase_messaging - sha256: "502233442839406198c34458a4ff71ca3350cc7be88ce06a8b729cbd2162ef57" - url: "https://pub.dev" - source: hosted - version: "14.8.0" - firebase_messaging_platform_interface: - dependency: transitive - description: - name: firebase_messaging_platform_interface - sha256: "9438353a857c8000b0680d7ee246acb14fb854c4a14df4ebc7e1efde166903ac" - url: "https://pub.dev" - source: hosted - version: "4.5.30" - firebase_messaging_web: - dependency: transitive - description: - name: firebase_messaging_web - sha256: "6672c1c41e79d607b1ce0bbf1c6dcf97f7894b98bf65fe806e40d62a700bae3a" - url: "https://pub.dev" - source: hosted - version: "3.8.0" - fixnum: - dependency: transitive - description: - name: fixnum - sha256: "25517a4deb0c03aa0f32fd12db525856438902d9c16536311e76cdc57b31d7d1" - url: "https://pub.dev" - source: hosted - version: "1.1.0" - flutter: - dependency: "direct main" - description: flutter - source: sdk - version: "0.0.0" - flutter_color_models: - dependency: "direct main" - description: - name: flutter_color_models - sha256: "8454198ba9a82e533452d0764f1df3d6c3ccc2b33e18eb1f2fedc7ae4a5c43be" - url: "https://pub.dev" - source: hosted - version: "1.3.3+2" - flutter_driver: - dependency: "direct dev" - description: flutter - source: sdk - version: "0.0.0" - flutter_lints: - dependency: "direct dev" - description: - name: flutter_lints - sha256: "9e8c3858111da373efc5aa341de011d9bd23e2c5c5e0c62bccf32438e192d7b1" - url: "https://pub.dev" - source: hosted - version: "3.0.2" - flutter_local_notifications: - dependency: "direct main" - description: - name: flutter_local_notifications - sha256: f9a05409385b77b06c18f200a41c7c2711ebf7415669350bb0f8474c07bd40d1 - url: "https://pub.dev" - source: hosted - version: "17.0.0" - flutter_local_notifications_linux: - dependency: transitive - description: - name: flutter_local_notifications_linux - sha256: "33f741ef47b5f63cc7f78fe75eeeac7e19f171ff3c3df054d84c1e38bedb6a03" - url: "https://pub.dev" - source: hosted - version: "4.0.0+1" - flutter_local_notifications_platform_interface: - dependency: "direct main" - description: - name: flutter_local_notifications_platform_interface - sha256: "7cf643d6d5022f3baed0be777b0662cce5919c0a7b86e700299f22dc4ae660ef" - url: "https://pub.dev" - source: hosted - version: "7.0.0+1" - flutter_localizations: - dependency: "direct main" - description: flutter - source: sdk - version: "0.0.0" - flutter_plugin_android_lifecycle: - dependency: transitive - description: - name: flutter_plugin_android_lifecycle - sha256: b068ffc46f82a55844acfa4fdbb61fad72fa2aef0905548419d97f0f95c456da - url: "https://pub.dev" - source: hosted - version: "2.0.17" - flutter_test: - dependency: "direct dev" - description: flutter - source: sdk - version: "0.0.0" - flutter_web_plugins: - dependency: transitive - description: flutter - source: sdk - version: "0.0.0" - frontend_server_client: - dependency: transitive - description: - name: frontend_server_client - sha256: f64a0333a82f30b0cca061bc3d143813a486dc086b574bfb233b7c1372427694 - url: "https://pub.dev" - source: hosted - version: "4.0.0" - fuchsia_remote_debug_protocol: - dependency: transitive - description: flutter - source: sdk - version: "0.0.0" - glob: - dependency: transitive - description: - name: glob - sha256: "0e7014b3b7d4dac1ca4d6114f82bf1782ee86745b9b42a92c9289c23d8a0ab63" - url: "https://pub.dev" - source: hosted - version: "2.1.2" - graphs: - dependency: transitive - description: - name: graphs - sha256: aedc5a15e78fc65a6e23bcd927f24c64dd995062bcd1ca6eda65a3cff92a4d19 - url: "https://pub.dev" - source: hosted - version: "2.3.1" - html: - dependency: "direct main" - description: - name: html - sha256: "3a7812d5bcd2894edf53dfaf8cd640876cf6cef50a8f238745c8b8120ea74d3a" - url: "https://pub.dev" - source: hosted - version: "0.15.4" - http: - dependency: "direct main" - description: - name: http - sha256: "761a297c042deedc1ffbb156d6e2af13886bb305c2a343a4d972504cd67dd938" - url: "https://pub.dev" - source: hosted - version: "1.2.1" - http_multi_server: - dependency: transitive - description: - name: http_multi_server - sha256: "97486f20f9c2f7be8f514851703d0119c3596d14ea63227af6f7a481ef2b2f8b" - url: "https://pub.dev" - source: hosted - version: "3.2.1" - http_parser: - dependency: transitive - description: - name: http_parser - sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b" - url: "https://pub.dev" - source: hosted - version: "4.0.2" - image_picker: - dependency: "direct main" - description: - name: image_picker - sha256: "26222b01a0c9a2c8fe02fc90b8208bd3325da5ed1f4a2acabf75939031ac0bdd" - url: "https://pub.dev" - source: hosted - version: "1.0.7" - image_picker_android: - dependency: transitive - description: - name: image_picker_android - sha256: "42c098e7fb6334746be37cdc30369ade356ed4f14d48b7a0313f95a9159f4321" - url: "https://pub.dev" - source: hosted - version: "0.8.9+5" - image_picker_for_web: - dependency: transitive - description: - name: image_picker_for_web - sha256: "6a1704fdd75022272e7e7a897a9068e9c2ff3cd6a66820bf3ded810633eac954" - url: "https://pub.dev" - source: hosted - version: "3.0.3" - image_picker_ios: - dependency: transitive - description: - name: image_picker_ios - sha256: "917a5cadd67d052554cfb258595e54217de53fac5b52939426e26319a02e6297" - url: "https://pub.dev" - source: hosted - version: "0.8.9+2" - image_picker_linux: - dependency: transitive - description: - name: image_picker_linux - sha256: "4ed1d9bb36f7cd60aa6e6cd479779cc56a4cb4e4de8f49d487b1aaad831300fa" - url: "https://pub.dev" - source: hosted - version: "0.2.1+1" - image_picker_macos: - dependency: transitive - description: - name: image_picker_macos - sha256: "3f5ad1e8112a9a6111c46d0b57a7be2286a9a07fc6e1976fdf5be2bd31d4ff62" - url: "https://pub.dev" - source: hosted - version: "0.2.1+1" - image_picker_platform_interface: - dependency: transitive - description: - name: image_picker_platform_interface - sha256: "3d2c323daea9d60608f1caf30be32a938916f4975434b8352e6f73dae496da38" - url: "https://pub.dev" - source: hosted - version: "2.9.4" - image_picker_windows: - dependency: transitive - description: - name: image_picker_windows - sha256: "6ad07afc4eb1bc25f3a01084d28520496c4a3bb0cb13685435838167c9dcedeb" - url: "https://pub.dev" - source: hosted - version: "0.2.1+1" - integration_test: - dependency: "direct dev" - description: flutter - source: sdk - version: "0.0.0" - intl: - dependency: "direct main" - description: - name: intl - sha256: d6f56758b7d3014a48af9701c085700aac781a92a87a62b1333b46d8879661cf - url: "https://pub.dev" - source: hosted - version: "0.19.0" - io: - dependency: transitive - description: - name: io - sha256: "2ec25704aba361659e10e3e5f5d672068d332fc8ac516421d483a11e5cbd061e" - url: "https://pub.dev" - source: hosted - version: "1.0.4" - js: - dependency: transitive - description: - name: js - sha256: c1b2e9b5ea78c45e1a0788d29606ba27dc5f71f019f32ca5140f61ef071838cf - url: "https://pub.dev" - source: hosted - version: "0.7.1" - json_annotation: - dependency: "direct main" - description: - name: json_annotation - sha256: b10a7b2ff83d83c777edba3c6a0f97045ddadd56c944e1a23a3fdf43a1bf4467 - url: "https://pub.dev" - source: hosted - version: "4.8.1" - json_serializable: - dependency: "direct dev" - description: - name: json_serializable - sha256: aa1f5a8912615733e0fdc7a02af03308933c93235bdc8d50d0b0c8a8ccb0b969 - url: "https://pub.dev" - source: hosted - version: "6.7.1" - leak_tracker: - dependency: transitive - description: - name: leak_tracker - sha256: "3f87a60e8c63aecc975dda1ceedbc8f24de75f09e4856ea27daf8958f2f0ce05" - url: "https://pub.dev" - source: hosted - version: "10.0.5" - leak_tracker_flutter_testing: - dependency: transitive - description: - name: leak_tracker_flutter_testing - sha256: "932549fb305594d82d7183ecd9fa93463e9914e1b67cacc34bc40906594a1806" - url: "https://pub.dev" - source: hosted - version: "3.0.5" - leak_tracker_testing: - dependency: transitive - description: - name: leak_tracker_testing - sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" - url: "https://pub.dev" - source: hosted - version: "3.0.1" - lints: - dependency: transitive - description: - name: lints - sha256: cbf8d4b858bb0134ef3ef87841abdf8d63bfc255c266b7bf6b39daa1085c4290 - url: "https://pub.dev" - source: hosted - version: "3.0.0" - logging: - dependency: transitive - description: - name: logging - sha256: "623a88c9594aa774443aa3eb2d41807a48486b5613e67599fb4c41c0ad47c340" - url: "https://pub.dev" - source: hosted - version: "1.2.0" - matcher: - dependency: transitive - description: - name: matcher - sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb - url: "https://pub.dev" - source: hosted - version: "0.12.16+1" - material_color_utilities: - dependency: transitive - description: - name: material_color_utilities - sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec - url: "https://pub.dev" - source: hosted - version: "0.11.1" - meta: - dependency: transitive - description: - name: meta - sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136" - url: "https://pub.dev" - source: hosted - version: "1.12.0" - mime: - dependency: transitive - description: - name: mime - sha256: "2e123074287cc9fd6c09de8336dae606d1ddb88d9ac47358826db698c176a1f2" - url: "https://pub.dev" - source: hosted - version: "1.0.5" - node_preamble: - dependency: transitive - description: - name: node_preamble - sha256: "6e7eac89047ab8a8d26cf16127b5ed26de65209847630400f9aefd7cd5c730db" - url: "https://pub.dev" - source: hosted - version: "2.0.2" - num_utilities: - dependency: transitive - description: - name: num_utilities - sha256: "170695bcafd17a19ee3a060325e7e10fb35bb878c8bcb3e6f5691dde1462c0ae" - url: "https://pub.dev" - source: hosted - version: "1.0.5" - package_config: - dependency: transitive - description: - name: package_config - sha256: "1c5b77ccc91e4823a5af61ee74e6b972db1ef98c2ff5a18d3161c982a55448bd" - url: "https://pub.dev" - source: hosted - version: "2.1.0" - package_info_plus: - dependency: "direct main" - description: - name: package_info_plus - sha256: cb44f49b6e690fa766f023d5b22cac6b9affe741dd792b6ac7ad4fabe0d7b097 - url: "https://pub.dev" - source: hosted - version: "6.0.0" - package_info_plus_platform_interface: - dependency: transitive - description: - name: package_info_plus_platform_interface - sha256: "9bc8ba46813a4cc42c66ab781470711781940780fd8beddd0c3da62506d3a6c6" - url: "https://pub.dev" - source: hosted - version: "2.0.1" - path: - dependency: "direct main" - description: - name: path - sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af" - url: "https://pub.dev" - source: hosted - version: "1.9.0" - path_provider: - dependency: "direct main" - description: - name: path_provider - sha256: b27217933eeeba8ff24845c34003b003b2b22151de3c908d0e679e8fe1aa078b - url: "https://pub.dev" - source: hosted - version: "2.1.2" - path_provider_android: - dependency: transitive - description: - name: path_provider_android - sha256: "477184d672607c0a3bf68fbbf601805f92ef79c82b64b4d6eb318cbca4c48668" - url: "https://pub.dev" - source: hosted - version: "2.2.2" - path_provider_foundation: - dependency: transitive - description: - name: path_provider_foundation - sha256: "5a7999be66e000916500be4f15a3633ebceb8302719b47b9cc49ce924125350f" - url: "https://pub.dev" - source: hosted - version: "2.3.2" - path_provider_linux: - dependency: transitive - description: - name: path_provider_linux - sha256: f7a1fe3a634fe7734c8d3f2766ad746ae2a2884abe22e241a8b301bf5cac3279 - url: "https://pub.dev" - source: hosted - version: "2.2.1" - path_provider_platform_interface: - dependency: transitive - description: - name: path_provider_platform_interface - sha256: "88f5779f72ba699763fa3a3b06aa4bf6de76c8e5de842cf6f29e2e06476c2334" - url: "https://pub.dev" - source: hosted - version: "2.1.2" - path_provider_windows: - dependency: transitive - description: - name: path_provider_windows - sha256: "8bc9f22eee8690981c22aa7fc602f5c85b497a6fb2ceb35ee5a5e5ed85ad8170" - url: "https://pub.dev" - source: hosted - version: "2.2.1" - petitparser: - dependency: transitive - description: - name: petitparser - sha256: c15605cd28af66339f8eb6fbe0e541bfe2d1b72d5825efc6598f3e0a31b9ad27 - url: "https://pub.dev" - source: hosted - version: "6.0.2" - pigeon: - dependency: "direct dev" - description: - name: pigeon - sha256: be883401d09121c427ed9c5f6e96427787d93d335f55e5e2b0d780a0a22cd561 - url: "https://pub.dev" - source: hosted - version: "18.0.0" - platform: - dependency: transitive - description: - name: platform - sha256: "12220bb4b65720483f8fa9450b4332347737cf8213dd2840d8b2c823e47243ec" - url: "https://pub.dev" - source: hosted - version: "3.1.4" - plugin_platform_interface: - dependency: transitive - description: - name: plugin_platform_interface - sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02" - url: "https://pub.dev" - source: hosted - version: "2.1.8" - pool: - dependency: transitive - description: - name: pool - sha256: "20fe868b6314b322ea036ba325e6fc0711a22948856475e2c2b6306e8ab39c2a" - url: "https://pub.dev" - source: hosted - version: "1.5.1" - powers: - dependency: transitive - description: - name: powers - sha256: "389ba222d4264655ecd3ffa461921bc707a07e4597b98831d21dd516eed6f496" - url: "https://pub.dev" - source: hosted - version: "1.0.0+2" - process: - dependency: transitive - description: - name: process - sha256: "21e54fd2faf1b5bdd5102afd25012184a6793927648ea81eea80552ac9405b32" - url: "https://pub.dev" - source: hosted - version: "5.0.2" - pub_semver: - dependency: transitive - description: - name: pub_semver - sha256: "40d3ab1bbd474c4c2328c91e3a7df8c6dd629b79ece4c4bd04bee496a224fb0c" - url: "https://pub.dev" - source: hosted - version: "2.1.4" - pubspec_parse: - dependency: transitive - description: - name: pubspec_parse - sha256: c63b2876e58e194e4b0828fcb080ad0e06d051cb607a6be51a9e084f47cb9367 - url: "https://pub.dev" - source: hosted - version: "1.2.3" - recase: - dependency: transitive - description: - name: recase - sha256: e4eb4ec2dcdee52dcf99cb4ceabaffc631d7424ee55e56f280bc039737f89213 - url: "https://pub.dev" - source: hosted - version: "4.1.0" - share_plus: - dependency: "direct main" - description: - name: share_plus - sha256: "05ec043470319bfbabe0adbc90d3a84cbff0426b9d9f3a6e2ad3e131fa5fa629" - url: "https://pub.dev" - source: hosted - version: "8.0.2" - share_plus_platform_interface: - dependency: "direct main" - description: - name: share_plus_platform_interface - sha256: "251eb156a8b5fa9ce033747d73535bf53911071f8d3b6f4f0b578505ce0d4496" - url: "https://pub.dev" - source: hosted - version: "3.4.0" - shelf: - dependency: transitive - description: - name: shelf - sha256: ad29c505aee705f41a4d8963641f91ac4cee3c8fad5947e033390a7bd8180fa4 - url: "https://pub.dev" - source: hosted - version: "1.4.1" - shelf_packages_handler: - dependency: transitive - description: - name: shelf_packages_handler - sha256: "89f967eca29607c933ba9571d838be31d67f53f6e4ee15147d5dc2934fee1b1e" - url: "https://pub.dev" - source: hosted - version: "3.0.2" - shelf_static: - dependency: transitive - description: - name: shelf_static - sha256: a41d3f53c4adf0f57480578c1d61d90342cd617de7fc8077b1304643c2d85c1e - url: "https://pub.dev" - source: hosted - version: "1.1.2" - shelf_web_socket: - dependency: transitive - description: - name: shelf_web_socket - sha256: "9ca081be41c60190ebcb4766b2486a7d50261db7bd0f5d9615f2d653637a84c1" - url: "https://pub.dev" - source: hosted - version: "1.0.4" - sky_engine: - dependency: transitive - description: flutter - source: sdk - version: "0.0.99" - source_gen: - dependency: transitive - description: - name: source_gen - sha256: "14658ba5f669685cd3d63701d01b31ea748310f7ab854e471962670abcf57832" - url: "https://pub.dev" - source: hosted - version: "1.5.0" - source_helper: - dependency: transitive - description: - name: source_helper - sha256: "6adebc0006c37dd63fe05bca0a929b99f06402fc95aa35bf36d67f5c06de01fd" - url: "https://pub.dev" - source: hosted - version: "1.3.4" - source_map_stack_trace: - dependency: transitive - description: - name: source_map_stack_trace - sha256: "84cf769ad83aa6bb61e0aa5a18e53aea683395f196a6f39c4c881fb90ed4f7ae" - url: "https://pub.dev" - source: hosted - version: "2.1.1" - source_maps: - dependency: transitive - description: - name: source_maps - sha256: "708b3f6b97248e5781f493b765c3337db11c5d2c81c3094f10904bfa8004c703" - url: "https://pub.dev" - source: hosted - version: "0.10.12" - source_span: - dependency: transitive - description: - name: source_span - sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" - url: "https://pub.dev" - source: hosted - version: "1.10.0" - sprintf: - dependency: transitive - description: - name: sprintf - sha256: "1fc9ffe69d4df602376b52949af107d8f5703b77cda567c4d7d86a0693120f23" - url: "https://pub.dev" - source: hosted - version: "7.0.0" - sqlite3: - dependency: "direct main" - description: - name: sqlite3 - sha256: "1abbeb84bf2b1a10e5e1138c913123c8aa9d83cd64e5f9a0dd847b3c83063202" - url: "https://pub.dev" - source: hosted - version: "2.4.2" - sqlite3_flutter_libs: - dependency: "direct main" - description: - name: sqlite3_flutter_libs - sha256: d6c31c8511c441d1f12f20b607343df1afe4eddf24a1cf85021677c8eea26060 - url: "https://pub.dev" - source: hosted - version: "0.5.20" - sqlparser: - dependency: transitive - description: - name: sqlparser - sha256: "7b20045d1ccfb7bc1df7e8f9fee5ae58673fce6ff62cefbb0e0fd7214e90e5a0" - url: "https://pub.dev" - source: hosted - version: "0.34.1" - stack_trace: - dependency: "direct dev" - description: - name: stack_trace - sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" - url: "https://pub.dev" - source: hosted - version: "1.11.1" - stream_channel: - dependency: transitive - description: - name: stream_channel - sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 - url: "https://pub.dev" - source: hosted - version: "2.1.2" - stream_transform: - dependency: transitive - description: - name: stream_transform - sha256: "14a00e794c7c11aa145a170587321aedce29769c08d7f58b1d141da75e3b1c6f" - url: "https://pub.dev" - source: hosted - version: "2.1.0" - string_scanner: - dependency: transitive - description: - name: string_scanner - sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" - url: "https://pub.dev" - source: hosted - version: "1.2.0" - sync_http: - dependency: transitive - description: - name: sync_http - sha256: "7f0cd72eca000d2e026bcd6f990b81d0ca06022ef4e32fb257b30d3d1014a961" - url: "https://pub.dev" - source: hosted - version: "0.3.1" - term_glyph: - dependency: transitive - description: - name: term_glyph - sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 - url: "https://pub.dev" - source: hosted - version: "1.2.1" - test: - dependency: "direct dev" - description: - name: test - sha256: "7ee446762c2c50b3bd4ea96fe13ffac69919352bd3b4b17bac3f3465edc58073" - url: "https://pub.dev" - source: hosted - version: "1.25.2" - test_api: - dependency: transitive - description: - name: test_api - sha256: "9955ae474176f7ac8ee4e989dadfb411a58c30415bcfb648fa04b2b8a03afa7f" - url: "https://pub.dev" - source: hosted - version: "0.7.0" - test_core: - dependency: transitive - description: - name: test_core - sha256: "2bc4b4ecddd75309300d8096f781c0e3280ca1ef85beda558d33fcbedc2eead4" - url: "https://pub.dev" - source: hosted - version: "0.6.0" - timezone: - dependency: transitive - description: - name: timezone - sha256: "1cfd8ddc2d1cfd836bc93e67b9be88c3adaeca6f40a00ca999104c30693cdca0" - url: "https://pub.dev" - source: hosted - version: "0.9.2" - timing: - dependency: transitive - description: - name: timing - sha256: "70a3b636575d4163c477e6de42f247a23b315ae20e86442bebe32d3cabf61c32" - url: "https://pub.dev" - source: hosted - version: "1.0.1" - typed_data: - dependency: transitive - description: - name: typed_data - sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c - url: "https://pub.dev" - source: hosted - version: "1.3.2" - url_launcher: - dependency: "direct main" - description: - name: url_launcher - sha256: "0ecc004c62fd3ed36a2ffcbe0dd9700aee63bd7532d0b642a488b1ec310f492e" - url: "https://pub.dev" - source: hosted - version: "6.2.5" - url_launcher_android: - dependency: "direct main" - description: - name: url_launcher_android - sha256: d4ed0711849dd8e33eb2dd69c25db0d0d3fdc37e0a62e629fe32f57a22db2745 - url: "https://pub.dev" - source: hosted - version: "6.3.0" - url_launcher_ios: - dependency: transitive - description: - name: url_launcher_ios - sha256: "9149d493b075ed740901f3ee844a38a00b33116c7c5c10d7fb27df8987fb51d5" - url: "https://pub.dev" - source: hosted - version: "6.2.5" - url_launcher_linux: - dependency: transitive - description: - name: url_launcher_linux - sha256: ab360eb661f8879369acac07b6bb3ff09d9471155357da8443fd5d3cf7363811 - url: "https://pub.dev" - source: hosted - version: "3.1.1" - url_launcher_macos: - dependency: transitive - description: - name: url_launcher_macos - sha256: b7244901ea3cf489c5335bdacda07264a6e960b1c1b1a9f91e4bc371d9e68234 - url: "https://pub.dev" - source: hosted - version: "3.1.0" - url_launcher_platform_interface: - dependency: transitive - description: - name: url_launcher_platform_interface - sha256: "552f8a1e663569be95a8190206a38187b531910283c3e982193e4f2733f01029" - url: "https://pub.dev" - source: hosted - version: "2.3.2" - url_launcher_web: - dependency: transitive - description: - name: url_launcher_web - sha256: "3692a459204a33e04bc94f5fb91158faf4f2c8903281ddd82915adecdb1a901d" - url: "https://pub.dev" - source: hosted - version: "2.3.0" - url_launcher_windows: - dependency: transitive - description: - name: url_launcher_windows - sha256: ecf9725510600aa2bb6d7ddabe16357691b6d2805f66216a97d1b881e21beff7 - url: "https://pub.dev" - source: hosted - version: "3.1.1" - uuid: - dependency: transitive - description: - name: uuid - sha256: cd210a09f7c18cbe5a02511718e0334de6559871052c90a90c0cca46a4aa81c8 - url: "https://pub.dev" - source: hosted - version: "4.3.3" - vector_math: - dependency: transitive - description: - name: vector_math - sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" - url: "https://pub.dev" - source: hosted - version: "2.1.4" - vm_service: - dependency: transitive - description: - name: vm_service - sha256: a75f83f14ad81d5fe4b3319710b90dec37da0e22612326b696c9e1b8f34bbf48 - url: "https://pub.dev" - source: hosted - version: "14.2.0" - watcher: - dependency: transitive - description: - name: watcher - sha256: "3d2ad6751b3c16cf07c7fca317a1413b3f26530319181b37e3b9039b84fc01d8" - url: "https://pub.dev" - source: hosted - version: "1.1.0" - web: - dependency: transitive - description: - name: web - sha256: "97da13628db363c635202ad97068d47c5b8aa555808e7a9411963c533b449b27" - url: "https://pub.dev" - source: hosted - version: "0.5.1" - web_socket_channel: - dependency: transitive - description: - name: web_socket_channel - sha256: "1d8e795e2a8b3730c41b8a98a2dff2e0fb57ae6f0764a1c46ec5915387d257b2" - url: "https://pub.dev" - source: hosted - version: "2.4.4" - webdriver: - dependency: transitive - description: - name: webdriver - sha256: "003d7da9519e1e5f329422b36c4dcdf18d7d2978d1ba099ea4e45ba490ed845e" - url: "https://pub.dev" - source: hosted - version: "3.0.3" - webkit_inspection_protocol: - dependency: transitive - description: - name: webkit_inspection_protocol - sha256: "87d3f2333bb240704cd3f1c6b5b7acd8a10e7f0bc28c28dcf14e782014f4a572" - url: "https://pub.dev" - source: hosted - version: "1.2.1" - win32: - dependency: transitive - description: - name: win32 - sha256: "0a989dc7ca2bb51eac91e8fd00851297cfffd641aa7538b165c62637ca0eaa4a" - url: "https://pub.dev" - source: hosted - version: "5.4.0" - win32_registry: - dependency: transitive - description: - name: win32_registry - sha256: "41fd8a189940d8696b1b810efb9abcf60827b6cbfab90b0c43e8439e3a39d85a" - url: "https://pub.dev" - source: hosted - version: "1.1.2" - xdg_directories: - dependency: transitive - description: - name: xdg_directories - sha256: faea9dee56b520b55a566385b84f2e8de55e7496104adada9962e0bd11bcff1d - url: "https://pub.dev" - source: hosted - version: "1.0.4" - xml: - dependency: transitive - description: - name: xml - sha256: b015a8ad1c488f66851d762d3090a21c600e479dc75e68328c52774040cf9226 - url: "https://pub.dev" - source: hosted - version: "6.5.0" - yaml: - dependency: transitive - description: - name: yaml - sha256: "75769501ea3489fca56601ff33454fe45507ea3bfb014161abc3b43ae25989d5" - url: "https://pub.dev" - source: hosted - version: "3.1.2" - zulip_plugin: - dependency: "direct main" - description: - path: "packages/zulip_plugin" - relative: true - source: path - version: "0.0.1" -sdks: - dart: ">=3.5.0-18.0.dev <4.0.0" - flutter: ">=3.22.0-5.0.pre.22" diff --git a/windows/flutter/CMakeLists.txt b/windows/flutter/CMakeLists.txt deleted file mode 100644 index 930d2071a3..0000000000 --- a/windows/flutter/CMakeLists.txt +++ /dev/null @@ -1,104 +0,0 @@ -# This file controls Flutter-level build steps. It should not be edited. -cmake_minimum_required(VERSION 3.14) - -set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral") - -# Configuration provided via flutter tool. -include(${EPHEMERAL_DIR}/generated_config.cmake) - -# TODO: Move the rest of this into files in ephemeral. See -# https://github.com/flutter/flutter/issues/57146. -set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper") - -# === Flutter Library === -set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/flutter_windows.dll") - -# Published to parent scope for install step. -set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE) -set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE) -set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE) -set(AOT_LIBRARY "${PROJECT_DIR}/build/windows/app.so" PARENT_SCOPE) - -list(APPEND FLUTTER_LIBRARY_HEADERS - "flutter_export.h" - "flutter_windows.h" - "flutter_messenger.h" - "flutter_plugin_registrar.h" - "flutter_texture_registrar.h" -) -list(TRANSFORM FLUTTER_LIBRARY_HEADERS PREPEND "${EPHEMERAL_DIR}/") -add_library(flutter INTERFACE) -target_include_directories(flutter INTERFACE - "${EPHEMERAL_DIR}" -) -target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}.lib") -add_dependencies(flutter flutter_assemble) - -# === Wrapper === -list(APPEND CPP_WRAPPER_SOURCES_CORE - "core_implementations.cc" - "standard_codec.cc" -) -list(TRANSFORM CPP_WRAPPER_SOURCES_CORE PREPEND "${WRAPPER_ROOT}/") -list(APPEND CPP_WRAPPER_SOURCES_PLUGIN - "plugin_registrar.cc" -) -list(TRANSFORM CPP_WRAPPER_SOURCES_PLUGIN PREPEND "${WRAPPER_ROOT}/") -list(APPEND CPP_WRAPPER_SOURCES_APP - "flutter_engine.cc" - "flutter_view_controller.cc" -) -list(TRANSFORM CPP_WRAPPER_SOURCES_APP PREPEND "${WRAPPER_ROOT}/") - -# Wrapper sources needed for a plugin. -add_library(flutter_wrapper_plugin STATIC - ${CPP_WRAPPER_SOURCES_CORE} - ${CPP_WRAPPER_SOURCES_PLUGIN} -) -apply_standard_settings(flutter_wrapper_plugin) -set_target_properties(flutter_wrapper_plugin PROPERTIES - POSITION_INDEPENDENT_CODE ON) -set_target_properties(flutter_wrapper_plugin PROPERTIES - CXX_VISIBILITY_PRESET hidden) -target_link_libraries(flutter_wrapper_plugin PUBLIC flutter) -target_include_directories(flutter_wrapper_plugin PUBLIC - "${WRAPPER_ROOT}/include" -) -add_dependencies(flutter_wrapper_plugin flutter_assemble) - -# Wrapper sources needed for the runner. -add_library(flutter_wrapper_app STATIC - ${CPP_WRAPPER_SOURCES_CORE} - ${CPP_WRAPPER_SOURCES_APP} -) -apply_standard_settings(flutter_wrapper_app) -target_link_libraries(flutter_wrapper_app PUBLIC flutter) -target_include_directories(flutter_wrapper_app PUBLIC - "${WRAPPER_ROOT}/include" -) -add_dependencies(flutter_wrapper_app flutter_assemble) - -# === Flutter tool backend === -# _phony_ is a non-existent file to force this command to run every time, -# since currently there's no way to get a full input/output list from the -# flutter tool. -set(PHONY_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/_phony_") -set_source_files_properties("${PHONY_OUTPUT}" PROPERTIES SYMBOLIC TRUE) -add_custom_command( - OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS} - ${CPP_WRAPPER_SOURCES_CORE} ${CPP_WRAPPER_SOURCES_PLUGIN} - ${CPP_WRAPPER_SOURCES_APP} - ${PHONY_OUTPUT} - COMMAND ${CMAKE_COMMAND} -E env - ${FLUTTER_TOOL_ENVIRONMENT} - "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat" - windows-x64 $ - VERBATIM -) -add_custom_target(flutter_assemble DEPENDS - "${FLUTTER_LIBRARY}" - ${FLUTTER_LIBRARY_HEADERS} - ${CPP_WRAPPER_SOURCES_CORE} - ${CPP_WRAPPER_SOURCES_PLUGIN} - ${CPP_WRAPPER_SOURCES_APP} -) diff --git a/windows/runner/flutter_window.cpp b/windows/runner/flutter_window.cpp deleted file mode 100644 index b25e363efa..0000000000 --- a/windows/runner/flutter_window.cpp +++ /dev/null @@ -1,66 +0,0 @@ -#include "flutter_window.h" - -#include - -#include "flutter/generated_plugin_registrant.h" - -FlutterWindow::FlutterWindow(const flutter::DartProject& project) - : project_(project) {} - -FlutterWindow::~FlutterWindow() {} - -bool FlutterWindow::OnCreate() { - if (!Win32Window::OnCreate()) { - return false; - } - - RECT frame = GetClientArea(); - - // The size here must match the window dimensions to avoid unnecessary surface - // creation / destruction in the startup path. - flutter_controller_ = std::make_unique( - frame.right - frame.left, frame.bottom - frame.top, project_); - // Ensure that basic setup of the controller was successful. - if (!flutter_controller_->engine() || !flutter_controller_->view()) { - return false; - } - RegisterPlugins(flutter_controller_->engine()); - SetChildContent(flutter_controller_->view()->GetNativeWindow()); - - flutter_controller_->engine()->SetNextFrameCallback([&]() { - this->Show(); - }); - - return true; -} - -void FlutterWindow::OnDestroy() { - if (flutter_controller_) { - flutter_controller_ = nullptr; - } - - Win32Window::OnDestroy(); -} - -LRESULT -FlutterWindow::MessageHandler(HWND hwnd, UINT const message, - WPARAM const wparam, - LPARAM const lparam) noexcept { - // Give Flutter, including plugins, an opportunity to handle window messages. - if (flutter_controller_) { - std::optional result = - flutter_controller_->HandleTopLevelWindowProc(hwnd, message, wparam, - lparam); - if (result) { - return *result; - } - } - - switch (message) { - case WM_FONTCHANGE: - flutter_controller_->engine()->ReloadSystemFonts(); - break; - } - - return Win32Window::MessageHandler(hwnd, message, wparam, lparam); -}