several toolbar UI tweaks
This commit is contained in:
parent
b7ab6038ac
commit
240b039ae7
15 changed files with 195 additions and 105 deletions
|
|
@ -4231,7 +4231,7 @@
|
|||
"inviteChat": "📨 Invite chat",
|
||||
"chatName": "Chat name",
|
||||
"reportContentIssueTitle": "Report content issue",
|
||||
"feedback": "Your feedback (optional)",
|
||||
"reportContentIssueDescription": "Sorry! AI can make personalized experiences but also may have issues. Please provide any feedback you have and we'll generate a new activity.",
|
||||
"feedback": "Optional feedback",
|
||||
"reportContentIssueDescription": "Uh oh! AI can faciliate personalized learning experiences but... also hallucinates. Please provide any feedback you have and we'll try again.",
|
||||
"clickTheWordAgainToDeselect": "Click the selected word to deselect it."
|
||||
}
|
||||
|
|
@ -22,7 +22,7 @@ void main() async {
|
|||
|
||||
// #Pangea
|
||||
try {
|
||||
await dotenv.load(fileName: ".env");
|
||||
await dotenv.load(fileName: ".env.local_choreo");
|
||||
} catch (e) {
|
||||
Logs().e('Failed to load .env file', e);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -225,6 +225,7 @@ class OriginalText extends StatelessWidget {
|
|||
controller.sourceText != null
|
||||
? Flexible(child: Text(controller.sourceText!))
|
||||
: const LinearProgressIndicator(),
|
||||
const SizedBox(width: 4),
|
||||
if (controller.isEditingSourceText)
|
||||
Expanded(
|
||||
child: TextField(
|
||||
|
|
@ -243,7 +244,7 @@ class OriginalText extends StatelessWidget {
|
|||
if (!controller.isEditingSourceText && controller.sourceText != null)
|
||||
AnimatedOpacity(
|
||||
duration: const Duration(milliseconds: 500),
|
||||
opacity: controller.nextITStep != null ? 1.0 : 0.0,
|
||||
opacity: controller.nextITStep != null ? 0.7 : 0.0,
|
||||
child: IconButton(
|
||||
onPressed: () => {
|
||||
if (controller.nextITStep != null)
|
||||
|
|
@ -252,6 +253,7 @@ class OriginalText extends StatelessWidget {
|
|||
},
|
||||
},
|
||||
icon: const Icon(Icons.edit_outlined),
|
||||
iconSize: 20,
|
||||
),
|
||||
),
|
||||
],
|
||||
|
|
|
|||
|
|
@ -113,6 +113,11 @@ class MyAnalyticsController extends BaseController<AnalyticsStream> {
|
|||
_pangeaController.analytics
|
||||
.filterConstructs(unfilteredConstructs: constructs)
|
||||
.then((filtered) {
|
||||
for (final use in filtered) {
|
||||
debugPrint(
|
||||
"_onNewAnalyticsData filtered use: ${use.constructType.string} ${use.useType.string} ${use.lemma} ${use.useType.pointValue}",
|
||||
);
|
||||
}
|
||||
if (filtered.isEmpty) return;
|
||||
|
||||
// @ggurdin - are we sure this isn't happening twice? it's also above
|
||||
|
|
@ -166,6 +171,14 @@ class MyAnalyticsController extends BaseController<AnalyticsStream> {
|
|||
}
|
||||
}
|
||||
|
||||
if (kDebugMode) {
|
||||
for (final use in uses) {
|
||||
debugPrint(
|
||||
"Draft use: ${use.constructType.string} ${use.useType.string} ${use.lemma} ${use.useType.pointValue}",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// @ggurdin - if the point of draft uses is that we don't want to send them twice,
|
||||
// then, if this is triggered here, couldn't that make a problem?
|
||||
final level = _pangeaController.analytics.level;
|
||||
|
|
@ -189,6 +202,7 @@ class MyAnalyticsController extends BaseController<AnalyticsStream> {
|
|||
/// cache of recently sent messages
|
||||
Future<void> _addLocalMessage(
|
||||
String eventID,
|
||||
// @ggurdin - why is this an eventID and not a roomID?
|
||||
List<OneConstructUse> constructs,
|
||||
) async {
|
||||
try {
|
||||
|
|
|
|||
|
|
@ -29,13 +29,14 @@ enum ConstructUseTypeEnum {
|
|||
/// encountered as distractor in IGC flow and selected it
|
||||
incIGC,
|
||||
|
||||
/// selected correctly in practice activity flow
|
||||
/// selected correctly in word meaning in context practice activity
|
||||
corPA,
|
||||
|
||||
/// encountered as distractor in practice activity flow and correctly ignored it
|
||||
/// encountered as distractor in word meaning in context practice activity and correctly ignored it
|
||||
/// Currently not used
|
||||
ignPA,
|
||||
|
||||
/// was target construct in practice activity but user did not select correctly
|
||||
/// was target construct in word meaning in context practice activity and incorrectly selected
|
||||
incPA,
|
||||
}
|
||||
|
||||
|
|
@ -125,9 +126,9 @@ extension ConstructUseTypeExtension on ConstructUseTypeEnum {
|
|||
case ConstructUseTypeEnum.unk:
|
||||
return 0;
|
||||
case ConstructUseTypeEnum.corPA:
|
||||
return 2;
|
||||
return 5;
|
||||
case ConstructUseTypeEnum.incPA:
|
||||
return -1;
|
||||
return -2;
|
||||
case ConstructUseTypeEnum.ignPA:
|
||||
return 1;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import 'package:fluffychat/pages/chat/events/audio_player.dart';
|
|||
import 'package:fluffychat/pangea/matrix_event_wrappers/pangea_message_event.dart';
|
||||
import 'package:fluffychat/pangea/utils/error_handler.dart';
|
||||
import 'package:fluffychat/pangea/widgets/chat/message_selection_overlay.dart';
|
||||
import 'package:fluffychat/pangea/widgets/chat/message_toolbar.dart';
|
||||
import 'package:fluffychat/pangea/widgets/chat/toolbar_content_loading_indicator.dart';
|
||||
import 'package:fluffychat/pangea/widgets/igc/card_error_widget.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
|
@ -86,6 +87,8 @@ class MessageAudioCardState extends State<MessageAudioCard> {
|
|||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
padding: const EdgeInsets.all(8),
|
||||
constraints: const BoxConstraints(minHeight: minCardHeight),
|
||||
alignment: Alignment.center,
|
||||
child: _isLoading
|
||||
? const ToolbarContentLoadingIndicator()
|
||||
: localAudioEvent != null || audioFile != null
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ import 'package:fluffychat/widgets/avatar.dart';
|
|||
import 'package:fluffychat/widgets/matrix.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/scheduler.dart';
|
||||
import 'package:matrix/matrix.dart';
|
||||
|
||||
class MessageSelectionOverlay extends StatefulWidget {
|
||||
|
|
@ -73,6 +74,26 @@ class MessageOverlayController extends State<MessageSelectionOverlay>
|
|||
setInitialToolbarMode();
|
||||
}
|
||||
|
||||
/// We need to check if the setState call is safe to call immediately
|
||||
/// Kept getting the error: setState() or markNeedsBuild() called during build.
|
||||
/// This is a workaround to prevent that error
|
||||
@override
|
||||
void setState(VoidCallback fn) {
|
||||
if (SchedulerBinding.instance.schedulerPhase == SchedulerPhase.idle ||
|
||||
SchedulerBinding.instance.schedulerPhase ==
|
||||
SchedulerPhase.postFrameCallbacks) {
|
||||
// It's safe to call setState immediately
|
||||
super.setState(fn);
|
||||
} else {
|
||||
// Defer the setState call to after the current frame
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
if (mounted) {
|
||||
super.setState(fn);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
bool get isPracticeComplete => activitiesLeftToComplete <= 0;
|
||||
|
||||
/// When an activity is completed, we need to update the state
|
||||
|
|
@ -137,7 +158,8 @@ class MessageOverlayController extends State<MessageSelectionOverlay>
|
|||
void onClickOverlayMessageToken(
|
||||
PangeaToken token,
|
||||
) {
|
||||
if (toolbarMode == MessageMode.practiceActivity) {
|
||||
if ([MessageMode.practiceActivity, MessageMode.textToSpeech]
|
||||
.contains(toolbarMode)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -13,10 +13,13 @@ import 'package:fluffychat/pangea/widgets/chat/message_translation_card.dart';
|
|||
import 'package:fluffychat/pangea/widgets/chat/message_unsubscribed_card.dart';
|
||||
import 'package:fluffychat/pangea/widgets/igc/word_data_card.dart';
|
||||
import 'package:fluffychat/pangea/widgets/practice_activity/practice_activity_card.dart';
|
||||
import 'package:fluffychat/pangea/widgets/select_to_define.dart';
|
||||
import 'package:fluffychat/widgets/matrix.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
const double minCardHeight = 70;
|
||||
|
||||
class MessageToolbar extends StatefulWidget {
|
||||
final PangeaMessageEvent pangeaMessageEvent;
|
||||
final MessageOverlayController overLayController;
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import 'package:fluffychat/pangea/repo/full_text_translation_repo.dart';
|
|||
import 'package:fluffychat/pangea/utils/bot_style.dart';
|
||||
import 'package:fluffychat/pangea/utils/error_handler.dart';
|
||||
import 'package:fluffychat/pangea/utils/inline_tooltip.dart';
|
||||
import 'package:fluffychat/pangea/widgets/chat/message_toolbar.dart';
|
||||
import 'package:fluffychat/pangea/widgets/chat/toolbar_content_loading_indicator.dart';
|
||||
import 'package:fluffychat/pangea/widgets/igc/card_error_widget.dart';
|
||||
import 'package:fluffychat/widgets/matrix.dart';
|
||||
|
|
@ -135,6 +136,8 @@ class MessageTranslationCardState extends State<MessageTranslationCard> {
|
|||
|
||||
return Container(
|
||||
padding: const EdgeInsets.all(8),
|
||||
constraints: const BoxConstraints(minHeight: minCardHeight),
|
||||
alignment: Alignment.center,
|
||||
child: _fetchingTranslation
|
||||
? const ToolbarContentLoadingIndicator()
|
||||
: Column(
|
||||
|
|
|
|||
89
lib/pangea/widgets/content_issue_button.dart
Normal file
89
lib/pangea/widgets/content_issue_button.dart
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
import 'package:fluffychat/pangea/widgets/common/bot_face_svg.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
||||
|
||||
class ContentIssueButton extends StatelessWidget {
|
||||
final bool isActive;
|
||||
final void Function(String) submitFeedback;
|
||||
|
||||
const ContentIssueButton({
|
||||
super.key,
|
||||
required this.isActive,
|
||||
required this.submitFeedback,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Opacity(
|
||||
opacity: 0.8, // Slight opacity
|
||||
child: Tooltip(
|
||||
message: L10n.of(context)!.reportContentIssueTitle,
|
||||
child: IconButton(
|
||||
icon: const Icon(Icons.flag),
|
||||
iconSize: 16,
|
||||
onPressed: () {
|
||||
if (!isActive) {
|
||||
return;
|
||||
}
|
||||
final TextEditingController feedbackController =
|
||||
TextEditingController();
|
||||
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return AlertDialog(
|
||||
title: Text(
|
||||
L10n.of(context)!.reportContentIssueTitle,
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
content: Container(
|
||||
constraints: const BoxConstraints(maxWidth: 300),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const BotFace(
|
||||
width: 60,
|
||||
expression: BotExpression.addled,
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
Text(L10n.of(context)!.reportContentIssueDescription),
|
||||
const SizedBox(height: 10),
|
||||
SizedBox(
|
||||
width: double.infinity,
|
||||
child: TextField(
|
||||
controller: feedbackController,
|
||||
decoration: InputDecoration(
|
||||
labelText: L10n.of(context)!.feedback,
|
||||
border: const OutlineInputBorder(),
|
||||
),
|
||||
maxLines: 4,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop(); // Close the dialog
|
||||
},
|
||||
child: Text(L10n.of(context)!.cancel),
|
||||
),
|
||||
ElevatedButton(
|
||||
onPressed: () {
|
||||
// Call the additional callback function
|
||||
submitFeedback(feedbackController.text);
|
||||
Navigator.of(context).pop(); // Close the dialog
|
||||
},
|
||||
child: Text(L10n.of(context)!.submit),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -143,6 +143,8 @@ class SpanCardState extends State<SpanCard> {
|
|||
}
|
||||
}
|
||||
|
||||
/// @ggurdin - this seems like it would be including the correct answer as well
|
||||
/// we only want to give this kind of points for ignored distractors
|
||||
/// Returns the list of choices that are not selected
|
||||
List<SpanChoice>? get ignoredMatches => widget.scm.pangeaMatch?.match.choices
|
||||
?.where((choice) => !choice.selected)
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import 'package:fluffychat/pangea/models/language_model.dart';
|
|||
import 'package:fluffychat/pangea/utils/bot_style.dart';
|
||||
import 'package:fluffychat/pangea/utils/error_handler.dart';
|
||||
import 'package:fluffychat/pangea/utils/firebase_analytics.dart';
|
||||
import 'package:fluffychat/pangea/widgets/chat/message_toolbar.dart';
|
||||
import 'package:fluffychat/pangea/widgets/common/p_circular_loader.dart';
|
||||
import 'package:fluffychat/widgets/matrix.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
|
@ -176,6 +177,8 @@ class WordDataCardView extends StatelessWidget {
|
|||
|
||||
return Container(
|
||||
padding: const EdgeInsets.all(8),
|
||||
constraints: const BoxConstraints(minHeight: minCardHeight),
|
||||
alignment: Alignment.center,
|
||||
child: Scrollbar(
|
||||
thumbVisibility: true,
|
||||
controller: scrollController,
|
||||
|
|
@ -400,25 +403,3 @@ class PartOfSpeechBlock extends StatelessWidget {
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
class SelectToDefine extends StatelessWidget {
|
||||
const SelectToDefine({
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Center(
|
||||
child: Container(
|
||||
height: 80,
|
||||
padding: const EdgeInsets.all(8),
|
||||
child: Center(
|
||||
child: Text(
|
||||
L10n.of(context)!.selectToDefine,
|
||||
style: BotStyle.text(context),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import 'package:fluffychat/pangea/utils/bot_style.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class StarAnimationWidget extends StatefulWidget {
|
||||
|
|
@ -42,8 +43,8 @@ class _StarAnimationWidgetState extends State<StarAnimationWidget>
|
|||
Widget build(BuildContext context) {
|
||||
return SizedBox(
|
||||
// Set constant height and width for the star container
|
||||
height: 80.0,
|
||||
width: 80.0,
|
||||
height: 60.0,
|
||||
width: 60.0,
|
||||
child: Center(
|
||||
child: AnimatedBuilder(
|
||||
animation: _controller,
|
||||
|
|
@ -74,6 +75,7 @@ class GamifiedTextWidget extends StatelessWidget {
|
|||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min, // Adjusts the size to fit children
|
||||
children: [
|
||||
const SizedBox(height: 10), // Spacing between the star and text
|
||||
// Star animation above the text
|
||||
const StarAnimationWidget(),
|
||||
const SizedBox(height: 10), // Spacing between the star and text
|
||||
|
|
@ -84,10 +86,7 @@ class GamifiedTextWidget extends StatelessWidget {
|
|||
padding: const EdgeInsets.all(8),
|
||||
child: Text(
|
||||
userMessage,
|
||||
style: const TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
style: BotStyle.text(context),
|
||||
textAlign: TextAlign.center, // Center-align the text
|
||||
),
|
||||
),
|
||||
|
|
|
|||
|
|
@ -13,6 +13,8 @@ import 'package:fluffychat/pangea/utils/bot_style.dart';
|
|||
import 'package:fluffychat/pangea/utils/error_handler.dart';
|
||||
import 'package:fluffychat/pangea/widgets/animations/gain_points.dart';
|
||||
import 'package:fluffychat/pangea/widgets/chat/message_selection_overlay.dart';
|
||||
import 'package:fluffychat/pangea/widgets/chat/message_toolbar.dart';
|
||||
import 'package:fluffychat/pangea/widgets/content_issue_button.dart';
|
||||
import 'package:fluffychat/pangea/widgets/practice_activity/multiple_choice_activity.dart';
|
||||
import 'package:fluffychat/pangea/widgets/practice_activity/no_more_practice_card.dart';
|
||||
import 'package:fluffychat/pangea/widgets/practice_activity/target_tokens_controller.dart';
|
||||
|
|
@ -54,7 +56,7 @@ class MessagePracticeActivityCardState extends State<PracticeActivityCard> {
|
|||
// Used to show an animation when the user completes an activity
|
||||
// while simultaneously fetching a new activity and not showing the loading spinner
|
||||
// until the appropriate time has passed to 'savor the joy'
|
||||
Duration appropriateTimeForJoy = const Duration(milliseconds: 1000);
|
||||
Duration appropriateTimeForJoy = const Duration(milliseconds: 1500);
|
||||
bool savoringTheJoy = false;
|
||||
|
||||
@override
|
||||
|
|
@ -65,7 +67,7 @@ class MessagePracticeActivityCardState extends State<PracticeActivityCard> {
|
|||
|
||||
void _updateFetchingActivity(bool value) {
|
||||
if (fetchingActivity == value) return;
|
||||
setState(() => fetchingActivity = value);
|
||||
if (mounted) setState(() => fetchingActivity = value);
|
||||
}
|
||||
|
||||
void _setPracticeActivity(PracticeActivityEvent? activity) {
|
||||
|
|
@ -177,19 +179,13 @@ class MessagePracticeActivityCardState extends State<PracticeActivityCard> {
|
|||
);
|
||||
|
||||
Future<void> _savorTheJoy() async {
|
||||
if (savoringTheJoy) {
|
||||
//should not happen
|
||||
debugger(when: kDebugMode);
|
||||
}
|
||||
savoringTheJoy = true;
|
||||
debugger(when: savoringTheJoy && kDebugMode);
|
||||
|
||||
debugPrint('Savoring the joy');
|
||||
setState(() => savoringTheJoy = true);
|
||||
|
||||
await Future.delayed(appropriateTimeForJoy);
|
||||
|
||||
savoringTheJoy = false;
|
||||
|
||||
debugPrint('Savoring the joy is over');
|
||||
if (mounted) setState(() => savoringTheJoy = false);
|
||||
}
|
||||
|
||||
/// Called when the user finishes an activity.
|
||||
|
|
@ -233,6 +229,7 @@ class MessagePracticeActivityCardState extends State<PracticeActivityCard> {
|
|||
|
||||
widget.overlayController.onActivityFinish();
|
||||
|
||||
//
|
||||
final Iterable<dynamic> result = await Future.wait([
|
||||
_savorTheJoy(),
|
||||
_fetchNewActivity(),
|
||||
|
|
@ -254,50 +251,6 @@ class MessagePracticeActivityCardState extends State<PracticeActivityCard> {
|
|||
}
|
||||
}
|
||||
|
||||
void onFlagClick(BuildContext context) {
|
||||
final TextEditingController feedbackController = TextEditingController();
|
||||
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return AlertDialog(
|
||||
title: Text(L10n.of(context)!.reportContentIssueTitle),
|
||||
content: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text(L10n.of(context)!.reportContentIssueDescription),
|
||||
const SizedBox(height: 10),
|
||||
TextField(
|
||||
controller: feedbackController,
|
||||
decoration: InputDecoration(
|
||||
labelText: L10n.of(context)!.feedback,
|
||||
border: const OutlineInputBorder(),
|
||||
),
|
||||
maxLines: 3,
|
||||
),
|
||||
],
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop(); // Close the dialog
|
||||
},
|
||||
child: Text(L10n.of(context)!.cancel),
|
||||
),
|
||||
ElevatedButton(
|
||||
onPressed: () {
|
||||
// Call the additional callback function
|
||||
submitFeedback(feedbackController.text);
|
||||
Navigator.of(context).pop(); // Close the dialog
|
||||
},
|
||||
child: Text(L10n.of(context)!.submit),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/// clear the current activity, record, and selection
|
||||
/// fetch a new activity, including the offending activity in the request
|
||||
void submitFeedback(String feedback) {
|
||||
|
|
@ -385,6 +338,7 @@ class MessagePracticeActivityCardState extends State<PracticeActivityCard> {
|
|||
constraints: const BoxConstraints(
|
||||
maxWidth: 350,
|
||||
minWidth: 350,
|
||||
minHeight: minCardHeight,
|
||||
),
|
||||
child: Stack(
|
||||
alignment: Alignment.center,
|
||||
|
|
@ -412,18 +366,9 @@ class MessagePracticeActivityCardState extends State<PracticeActivityCard> {
|
|||
Positioned(
|
||||
top: 0,
|
||||
right: 0,
|
||||
child: Opacity(
|
||||
opacity: 0.65, // Slight opacity
|
||||
child: Tooltip(
|
||||
message: L10n.of(context)!.reportContentIssueTitle,
|
||||
child: IconButton(
|
||||
padding: const EdgeInsets.all(2),
|
||||
icon: const Icon(Icons.flag),
|
||||
iconSize: 16,
|
||||
onPressed: () =>
|
||||
currentActivity == null ? null : onFlagClick(context),
|
||||
),
|
||||
),
|
||||
child: ContentIssueButton(
|
||||
isActive: currentActivity != null,
|
||||
submitFeedback: submitFeedback,
|
||||
),
|
||||
),
|
||||
],
|
||||
|
|
|
|||
26
lib/pangea/widgets/select_to_define.dart
Normal file
26
lib/pangea/widgets/select_to_define.dart
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
import 'package:fluffychat/pangea/utils/bot_style.dart';
|
||||
import 'package:fluffychat/pangea/widgets/chat/message_toolbar.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
||||
|
||||
class SelectToDefine extends StatelessWidget {
|
||||
const SelectToDefine({
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Center(
|
||||
child: Container(
|
||||
constraints: const BoxConstraints(minHeight: minCardHeight),
|
||||
padding: const EdgeInsets.all(8),
|
||||
child: Center(
|
||||
child: Text(
|
||||
L10n.of(context)!.selectToDefine,
|
||||
style: BotStyle.text(context),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue