Fixed texts, added auto scroll
This commit is contained in:
parent
55325b260d
commit
fc9ab973d2
@ -12,6 +12,7 @@ const String systemPrmpt =
|
||||
At the start you will be provided a script with a lesson to cover.
|
||||
Keep the analysis short but the lesson can be as long as needed.
|
||||
Student is 15 years old. You can only interact using text, no videos, images, or audio.
|
||||
Make the lesson more in the style of a lecture, with you explaining the topic and the student asking questions.
|
||||
|
||||
After completing the theoretical part there's a quiz, you can start it yourself at the appropriate time or react to users' request by including <QUIZ_START_TOKEN> at the start of your response
|
||||
|
||||
@ -81,11 +82,6 @@ class Message {
|
||||
}
|
||||
|
||||
Content toGeminiContent() {
|
||||
// if (source == MessageSource.user || type == MessageType.lessonScript) {
|
||||
// return Content.text(text);
|
||||
// } else {
|
||||
// return Content.model([TextPart(text)]);
|
||||
// }
|
||||
switch (type) {
|
||||
case MessageType.text:
|
||||
if (source == MessageSource.user) {
|
||||
@ -187,7 +183,7 @@ class GeminiCubit extends Cubit<GeminiState> {
|
||||
// final String prompt =
|
||||
// "Jesteś nauczycielem/chatbotem prowadzącym zajęcia z jednym uczniem. Uczeń ma możliwość zadawania pytań w trakcie, natomiast jesteś odpowiedzialny za prowadzenie lekcji i przedstawienie tematu. Zacznij prowadzić lekcje dla jednego ucznia na podstawie poniszego skryptu:\n$rjp";
|
||||
final String prompt =
|
||||
"You are a teacher/chatbot conducting a class with one student. The student has the ability to ask questions during the lesson, while you are responsible for leading the class and presenting the topic. Start conducting the lesson for one student based on the script below:\n$lessonScript";
|
||||
"You are a teacher/chatbot conducting a class with one student. The student has the ability to ask questions during the lesson, while you are responsible for leading the class and presenting the topic. Start conducting the lecture for one student based on the script below:\n$lessonScript";
|
||||
|
||||
final safetySettings = [
|
||||
SafetySetting(HarmCategory.harassment, HarmBlockThreshold.none),
|
||||
|
@ -1 +1 @@
|
||||
const bool isDebug = true;
|
||||
const bool isDebug = false;
|
||||
|
@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
|
||||
import 'package:gemini_app/eeg/eeg_service.dart';
|
||||
import 'package:gemini_app/screens/eeg_calibration_screen.dart';
|
||||
import 'package:get_it/get_it.dart';
|
||||
import 'package:google_fonts/google_fonts.dart';
|
||||
|
||||
void main() {
|
||||
GetIt.I.registerSingleton<EegService>(EegService());
|
||||
@ -16,8 +17,11 @@ class MyApp extends StatelessWidget {
|
||||
return MaterialApp(
|
||||
title: 'MindEasy',
|
||||
theme: ThemeData(
|
||||
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
|
||||
useMaterial3: true,
|
||||
brightness: Brightness.dark,
|
||||
primarySwatch: Colors.teal,
|
||||
textTheme: GoogleFonts.openSansTextTheme(
|
||||
Theme.of(context).textTheme,
|
||||
),
|
||||
),
|
||||
home: const MindWanderScreen(),
|
||||
);
|
||||
|
@ -70,19 +70,22 @@ class MindWanderScreenState extends State<MindWanderScreen>
|
||||
AnimatedBuilder(
|
||||
animation: _animation,
|
||||
builder: (context, child) {
|
||||
return Opacity(
|
||||
opacity: _animation.value,
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
gradient: LinearGradient(
|
||||
begin: Alignment.topLeft,
|
||||
end: Alignment.bottomRight,
|
||||
colors: [
|
||||
Colors.blue.withOpacity(0.5),
|
||||
Colors.green.withOpacity(0.5),
|
||||
],
|
||||
transform:
|
||||
GradientRotation(_animation.value * 2 * 3.1415926535),
|
||||
return Container(
|
||||
color: Colors.white,
|
||||
child: Opacity(
|
||||
opacity: _animation.value,
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
gradient: LinearGradient(
|
||||
begin: Alignment.topLeft,
|
||||
end: Alignment.bottomRight,
|
||||
colors: [
|
||||
Colors.blue.withOpacity(0.5),
|
||||
Colors.green.withOpacity(0.5),
|
||||
],
|
||||
transform: GradientRotation(
|
||||
_animation.value * 2 * 3.1415926535),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
@ -5,6 +5,7 @@ import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:gemini_app/config.dart';
|
||||
import 'package:gemini_app/eeg/eeg_service.dart';
|
||||
import 'package:get_it/get_it.dart';
|
||||
import 'package:google_fonts/google_fonts.dart';
|
||||
|
||||
class GeminiScreen extends StatelessWidget {
|
||||
const GeminiScreen({super.key, required this.lessonId});
|
||||
@ -35,8 +36,9 @@ class GeminiChat extends StatefulWidget {
|
||||
|
||||
class GeminiChatState extends State<GeminiChat> {
|
||||
final _textController = TextEditingController();
|
||||
bool _quizMode = false; // Add this line
|
||||
bool _quizMode = false;
|
||||
final EegService _eegService = GetIt.instance<EegService>();
|
||||
final ScrollController _scrollController = ScrollController();
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
@ -57,6 +59,7 @@ class GeminiChatState extends State<GeminiChat> {
|
||||
@override
|
||||
void dispose() {
|
||||
_eegService.stopPolling();
|
||||
_scrollController.dispose(); // Add this line
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@ -67,6 +70,20 @@ class GeminiChatState extends State<GeminiChat> {
|
||||
void _sendMessage() async {
|
||||
context.read<GeminiCubit>().sendMessage(_textController.text);
|
||||
_textController.clear();
|
||||
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
_scrollToBottom();
|
||||
});
|
||||
}
|
||||
|
||||
void _scrollToBottom() {
|
||||
if (_scrollController.hasClients) {
|
||||
_scrollController.animateTo(
|
||||
_scrollController.position.maxScrollExtent,
|
||||
duration: const Duration(milliseconds: 300),
|
||||
curve: Curves.easeOut,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void _toggleEegState() {
|
||||
@ -108,9 +125,21 @@ class GeminiChatState extends State<GeminiChat> {
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'Mind Wandering: ${_eegService.state.mindWandering.toStringAsFixed(2)}'),
|
||||
'Mind Wandering: ${_eegService.state.mindWandering.toStringAsFixed(2)}',
|
||||
style: GoogleFonts.roboto(
|
||||
textStyle: const TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.white),
|
||||
)),
|
||||
Text(
|
||||
'Focus: ${_eegService.state.focus.toStringAsFixed(2)}'),
|
||||
'Focus: ${_eegService.state.focus.toStringAsFixed(2)}',
|
||||
style: GoogleFonts.roboto(
|
||||
textStyle: const TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.white),
|
||||
)),
|
||||
],
|
||||
),
|
||||
),
|
||||
@ -120,6 +149,10 @@ class GeminiChatState extends State<GeminiChat> {
|
||||
Expanded(
|
||||
child: BlocBuilder<GeminiCubit, GeminiState>(
|
||||
builder: (context, state) {
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
_scrollToBottom();
|
||||
});
|
||||
|
||||
if (state.status == GeminiStatus.loading) {
|
||||
return buildChatList(state, loading: true);
|
||||
} else if (state.status == GeminiStatus.error) {
|
||||
@ -137,15 +170,18 @@ class GeminiChatState extends State<GeminiChat> {
|
||||
? Container()
|
||||
: TextField(
|
||||
controller: _textController,
|
||||
style: TextStyle(color: Colors.white),
|
||||
decoration: InputDecoration(
|
||||
hintText: 'Enter your message',
|
||||
hintStyle: TextStyle(color: Colors.grey),
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(10.0),
|
||||
borderRadius: BorderRadius.circular(20.0),
|
||||
borderSide: BorderSide.none,
|
||||
),
|
||||
filled: true,
|
||||
fillColor: Colors.grey[200],
|
||||
// fillColor: Colors.grey[200],
|
||||
contentPadding: const EdgeInsets.symmetric(
|
||||
horizontal: 16.0, vertical: 12.0),
|
||||
horizontal: 20.0, vertical: 14.0),
|
||||
),
|
||||
onSubmitted: (_) => _sendMessage(),
|
||||
);
|
||||
@ -153,6 +189,7 @@ class GeminiChatState extends State<GeminiChat> {
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
children: [
|
||||
ElevatedButton(
|
||||
onPressed: _resetConversation,
|
||||
@ -176,19 +213,18 @@ class GeminiChatState extends State<GeminiChat> {
|
||||
],
|
||||
),
|
||||
),
|
||||
isDebug
|
||||
? ElevatedButton(
|
||||
onPressed: _toggleEegState,
|
||||
child: const Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Icon(Icons.toggle_on),
|
||||
SizedBox(width: 3),
|
||||
Text('Toggle State'),
|
||||
],
|
||||
),
|
||||
)
|
||||
: Container(),
|
||||
if (isDebug)
|
||||
ElevatedButton(
|
||||
onPressed: _toggleEegState,
|
||||
child: const Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Icon(Icons.toggle_on),
|
||||
SizedBox(width: 3),
|
||||
Text('Toggle State'),
|
||||
],
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
],
|
||||
@ -200,6 +236,7 @@ class GeminiChatState extends State<GeminiChat> {
|
||||
ListView buildChatList(GeminiState state, {bool loading = false}) {
|
||||
return ListView.builder(
|
||||
itemCount: state.messages.length + (loading ? 1 : 0),
|
||||
controller: _scrollController,
|
||||
itemBuilder: (context, index) {
|
||||
if (index == state.messages.length && loading) {
|
||||
return const Card(
|
||||
@ -230,7 +267,16 @@ class GeminiChatState extends State<GeminiChat> {
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
MarkdownBody(data: text),
|
||||
MarkdownBody(
|
||||
data: text,
|
||||
styleSheet: MarkdownStyleSheet(
|
||||
p: GoogleFonts.roboto(
|
||||
textStyle: const TextStyle(
|
||||
fontSize: 18,
|
||||
color: Colors.white,
|
||||
fontWeight: FontWeight.w300)),
|
||||
)),
|
||||
const SizedBox(height: 16),
|
||||
...message.quizOptions!.asMap().entries.map((entry) {
|
||||
return ElevatedButton(
|
||||
onPressed: () => {_checkAnswer(entry.key)},
|
||||
@ -271,7 +317,17 @@ class GeminiChatState extends State<GeminiChat> {
|
||||
crossAxisAlignment: message.source == MessageSource.agent
|
||||
? CrossAxisAlignment.start
|
||||
: CrossAxisAlignment.end,
|
||||
children: [MarkdownBody(data: text)],
|
||||
children: [
|
||||
MarkdownBody(
|
||||
data: text,
|
||||
styleSheet: MarkdownStyleSheet(
|
||||
p: GoogleFonts.roboto(
|
||||
textStyle: const TextStyle(
|
||||
fontSize: 18,
|
||||
color: Colors.white,
|
||||
fontWeight: FontWeight.w300)),
|
||||
))
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
|
@ -1,9 +1,11 @@
|
||||
import 'dart:convert';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_staggered_animations/flutter_staggered_animations.dart';
|
||||
import 'package:gemini_app/lesson.dart';
|
||||
import 'package:gemini_app/main.dart';
|
||||
import 'package:gemini_app/screens/gemini_chat_screen.dart';
|
||||
import 'package:google_fonts/google_fonts.dart';
|
||||
|
||||
class LessonListScreen extends StatefulWidget {
|
||||
@override
|
||||
@ -34,26 +36,49 @@ class _LessonListScreenState extends State<LessonListScreen> {
|
||||
appBar: AppBar(
|
||||
title: const Text('Available Lessons'),
|
||||
),
|
||||
body: ListView.separated(
|
||||
itemCount: lessons.length,
|
||||
itemBuilder: (context, index) {
|
||||
final lesson = lessons[index];
|
||||
return ListTile(
|
||||
title: Text(lesson.title),
|
||||
subtitle: Text(lesson.content),
|
||||
onTap: () {
|
||||
Navigator.push(
|
||||
context,
|
||||
createSmoothRoute(
|
||||
GeminiScreen(lessonId: lesson.id.toString()),
|
||||
body: AnimationLimiter(
|
||||
child: ListView.separated(
|
||||
itemCount: lessons.length,
|
||||
separatorBuilder: (context, index) => const Divider(),
|
||||
itemBuilder: (context, index) {
|
||||
final lesson = lessons[index];
|
||||
return AnimationConfiguration.staggeredList(
|
||||
position: index,
|
||||
duration: const Duration(milliseconds: 375),
|
||||
child: SlideAnimation(
|
||||
verticalOffset: 50.0,
|
||||
child: FadeInAnimation(
|
||||
child: Card(
|
||||
elevation: 4,
|
||||
margin:
|
||||
const EdgeInsets.symmetric(vertical: 8, horizontal: 16),
|
||||
child: ListTile(
|
||||
title: Text(
|
||||
lesson.title,
|
||||
style: GoogleFonts.roboto(
|
||||
textStyle: const TextStyle(
|
||||
fontSize: 18, fontWeight: FontWeight.bold),
|
||||
),
|
||||
),
|
||||
subtitle: Text(
|
||||
lesson.content,
|
||||
style: GoogleFonts.roboto(fontSize: 14),
|
||||
),
|
||||
onTap: () {
|
||||
Navigator.push(
|
||||
context,
|
||||
createSmoothRoute(
|
||||
GeminiScreen(lessonId: lesson.id.toString()),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
separatorBuilder: (context, index) {
|
||||
return Divider(); // or any other widget you want to use as a separator
|
||||
},
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
@ -5,6 +5,8 @@
|
||||
import FlutterMacOS
|
||||
import Foundation
|
||||
|
||||
import path_provider_foundation
|
||||
|
||||
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
||||
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
|
||||
}
|
||||
|
106
pubspec.lock
106
pubspec.lock
@ -57,6 +57,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.18.0"
|
||||
crypto:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: crypto
|
||||
sha256: ff625774173754681d66daaf4a448684fb04b78f902da9cb3d308c19cc5e8bab
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.3"
|
||||
cupertino_icons:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@ -73,6 +81,14 @@ packages:
|
||||
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"
|
||||
flutter:
|
||||
dependency: "direct main"
|
||||
description: flutter
|
||||
@ -102,6 +118,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.7.3"
|
||||
flutter_staggered_animations:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: flutter_staggered_animations
|
||||
sha256: "81d3c816c9bb0dca9e8a5d5454610e21ffb068aedb2bde49d2f8d04f75538351"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.1"
|
||||
flutter_test:
|
||||
dependency: "direct dev"
|
||||
description: flutter
|
||||
@ -115,6 +139,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "7.7.0"
|
||||
google_fonts:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: google_fonts
|
||||
sha256: b1ac0fe2832c9cc95e5e88b57d627c5e68c223b9657f4b96e1487aa9098c7b82
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.2.1"
|
||||
google_generative_ai:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@ -219,6 +251,70 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.9.0"
|
||||
path_provider:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path_provider
|
||||
sha256: fec0d61223fba3154d87759e3cc27fe2c8dc498f6386c6d6fc80d1afdd1bf378
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.4"
|
||||
path_provider_android:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path_provider_android
|
||||
sha256: "490539678396d4c3c0b06efdaab75ae60675c3e0c66f72bc04c2e2c1e0e2abeb"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.2.9"
|
||||
path_provider_foundation:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path_provider_foundation
|
||||
sha256: f234384a3fdd67f989b4d54a5d73ca2a6c422fa55ae694381ae0f4375cd1ea16
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.0"
|
||||
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: bd6f00dbd873bfb70d0761682da2b3a2c2fccc2b9e84c495821639601d81afe7
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.3.0"
|
||||
platform:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: platform
|
||||
sha256: "9b71283fc13df574056616011fb138fd3b793ea47cc509c189a6c3fa5f8a1a65"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.1.5"
|
||||
plugin_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: plugin_platform_interface
|
||||
sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.8"
|
||||
provider:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -312,6 +408,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.5.1"
|
||||
xdg_directories:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: xdg_directories
|
||||
sha256: faea9dee56b520b55a566385b84f2e8de55e7496104adada9962e0bd11bcff1d
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.4"
|
||||
sdks:
|
||||
dart: ">=3.4.1 <4.0.0"
|
||||
flutter: ">=3.19.0"
|
||||
flutter: ">=3.22.0"
|
||||
|
@ -44,6 +44,8 @@ dependencies:
|
||||
get_it: ^7.7.0
|
||||
http: ^1.2.2
|
||||
bloc: ^8.1.4
|
||||
flutter_staggered_animations: ^1.1.1
|
||||
google_fonts: ^6.2.1
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
|
Loading…
Reference in New Issue
Block a user