Added update checker, updated desktop view

This commit is contained in:
JHubi1 2024-06-05 18:25:29 +02:00
parent 08d566e855
commit 0a996e6ed2
No known key found for this signature in database
GPG Key ID: 7BF82570CBBBD050
6 changed files with 512 additions and 215 deletions

View File

@ -317,6 +317,47 @@
"description": "Text displayed when chats are imported successfully", "description": "Text displayed when chats are imported successfully",
"context": "Visible in the settings view" "context": "Visible in the settings view"
}, },
"settingsUpdateCheck": "Check for updates",
"@settingsUpdateCheck": {
"description": "Text displayed as description for check for updates button",
"context": "Visible in the settings view"
},
"settingsUpdateChecking": "Checking for updates ...",
"@settingsUpdateChecking": {
"description": "Text displayed while looking for updates",
"context": "Visible in the settings view"
},
"settingsUpdateLatest": "You are on the latest version",
"@settingsUpdateLatest": {
"description": "Text displayed when the app is up to date",
"context": "Visible in the settings view"
},
"settingsUpdateAvailable": "Update available (v{version})",
"@settingsUpdateAvailable": {
"description": "Text displayed when an update is available",
"context": "Visible in the settings view",
"placeholders": {
"version": {
"type": "String",
"description": "Version number of the available update"
}
}
},
"settingsUpdateRateLimit": "API rate limit exceeded. Not able to check for updates",
"@settingsUpdateRateLimit": {
"description": "Text displayed when the API rate limit is exceeded",
"context": "Visible in the settings view"
},
"settingsUpdateIssue": "An issue occurred while checking for updates. Click to retry",
"@settingsUpdateIssue": {
"description": "Text displayed when an issue occurs while checking for updates",
"context": "Visible in the settings view"
},
"settingsCheckForUpdates": "Check for update on settings open",
"@settingsCheckForUpdates": {
"description": "Text displayed as description for check for updates toggle",
"context": "Visible in the settings view"
},
"settingsGithub": "GitHub", "settingsGithub": "GitHub",
"@settingsGithub": { "@settingsGithub": {
"description": "Text displayed as description for GitHub button", "description": "Text displayed as description for GitHub button",

View File

@ -64,6 +64,8 @@ bool chatAllowed = true;
final user = types.User(id: const Uuid().v4()); final user = types.User(id: const Uuid().v4());
final assistant = types.User(id: const Uuid().v4()); final assistant = types.User(id: const Uuid().v4());
bool settingsOpen = false;
void main() { void main() {
runApp(const App()); runApp(const App());
@ -92,7 +94,9 @@ class _AppState extends State<App> {
super.initState(); super.initState();
void load() async { void load() async {
try {
await FlutterDisplayMode.setHighRefreshRate(); await FlutterDisplayMode.setHighRefreshRate();
} catch (_) {}
SharedPreferences.setPrefix("ollama."); SharedPreferences.setPrefix("ollama.");
SharedPreferences tmp = await SharedPreferences.getInstance(); SharedPreferences tmp = await SharedPreferences.getInstance();
setState(() { setState(() {
@ -215,7 +219,12 @@ class _MainAppState extends State<MainApp> {
borderRadius: BorderRadius.all(Radius.circular(50))), borderRadius: BorderRadius.all(Radius.circular(50))),
onTap: () { onTap: () {
HapticFeedback.selectionClick(); HapticFeedback.selectionClick();
if (!(Platform.isWindows ||
Platform.isLinux ||
Platform.isMacOS) &&
MediaQuery.of(context).size.width <= 1000) {
Navigator.of(context).pop(); Navigator.of(context).pop();
}
if (!chatAllowed) return; if (!chatAllowed) return;
chatUuid = null; chatUuid = null;
messages = []; messages = [];
@ -246,9 +255,14 @@ class _MainAppState extends State<MainApp> {
borderRadius: BorderRadius.all(Radius.circular(50))), borderRadius: BorderRadius.all(Radius.circular(50))),
onTap: () { onTap: () {
HapticFeedback.selectionClick(); HapticFeedback.selectionClick();
if (!(Platform.isWindows ||
Platform.isLinux ||
Platform.isMacOS) &&
MediaQuery.of(context).size.width <= 1000) {
Navigator.of(context).pop(); Navigator.of(context).pop();
}
setState(() { setState(() {
logoVisible = false; settingsOpen = true;
}); });
Navigator.push( Navigator.push(
context, context,
@ -328,6 +342,7 @@ class _MainAppState extends State<MainApp> {
splashFactory: NoSplash.splashFactory, splashFactory: NoSplash.splashFactory,
highlightColor: Colors.transparent, highlightColor: Colors.transparent,
enableFeedback: false, enableFeedback: false,
hoverColor: Colors.transparent,
onTap: () { onTap: () {
HapticFeedback.selectionClick(); HapticFeedback.selectionClick();
setState(() { setState(() {
@ -422,8 +437,13 @@ class _MainAppState extends State<MainApp> {
if (chatUuid == jsonDecode(item)["uuid"]) { if (chatUuid == jsonDecode(item)["uuid"]) {
messages = []; messages = [];
chatUuid = null; chatUuid = null;
if (!(Platform.isWindows ||
Platform.isLinux ||
Platform.isMacOS) &&
MediaQuery.of(context).size.width <= 1000) {
Navigator.of(context).pop(); Navigator.of(context).pop();
} }
}
setState(() {}); setState(() {});
}, },
child: Padding( child: Padding(
@ -433,7 +453,12 @@ class _MainAppState extends State<MainApp> {
borderRadius: BorderRadius.all(Radius.circular(50))), borderRadius: BorderRadius.all(Radius.circular(50))),
onTap: () { onTap: () {
HapticFeedback.selectionClick(); HapticFeedback.selectionClick();
if (!(Platform.isWindows ||
Platform.isLinux ||
Platform.isMacOS) &&
MediaQuery.of(context).size.width <= 1000) {
Navigator.of(context).pop(); Navigator.of(context).pop();
}
if (!chatAllowed) return; if (!chatAllowed) return;
loadChat(jsonDecode(item)["uuid"], setState); loadChat(jsonDecode(item)["uuid"], setState);
chatUuid = jsonDecode(item)["uuid"]; chatUuid = jsonDecode(item)["uuid"];
@ -609,6 +634,7 @@ class _MainAppState extends State<MainApp> {
splashFactory: NoSplash.splashFactory, splashFactory: NoSplash.splashFactory,
highlightColor: Colors.transparent, highlightColor: Colors.transparent,
enableFeedback: false, enableFeedback: false,
hoverColor: Colors.transparent,
child: SizedBox( child: SizedBox(
height: 200, height: 200,
child: Row( child: Row(
@ -787,6 +813,19 @@ class _MainAppState extends State<MainApp> {
preferredSize: const Size.fromHeight(1), preferredSize: const Size.fromHeight(1),
child: (!chatAllowed && model != null) child: (!chatAllowed && model != null)
? const LinearProgressIndicator() ? const LinearProgressIndicator()
: ((Platform.isWindows ||
Platform.isLinux ||
Platform.isMacOS) &&
MediaQuery.of(context).size.width >= 1000)
? AnimatedOpacity(
opacity: menuVisible ? 1.0 : 0.0,
duration: const Duration(milliseconds: 500),
child: Divider(
height: 2,
color: (Theme.of(context).brightness ==
Brightness.light)
? Colors.grey[400]
: Colors.grey[900]))
: const SizedBox.shrink()), : const SizedBox.shrink()),
leading: ((Platform.isWindows || leading: ((Platform.isWindows ||
Platform.isLinux || Platform.isLinux ||
@ -806,6 +845,7 @@ class _MainAppState extends State<MainApp> {
child: VisibilityDetector( child: VisibilityDetector(
key: const Key("menuVisible"), key: const Key("menuVisible"),
onVisibilityChanged: (VisibilityInfo info) { onVisibilityChanged: (VisibilityInfo info) {
if (settingsOpen) return;
menuVisible = info.visibleFraction > 0; menuVisible = info.visibleFraction > 0;
try { try {
setState(() {}); setState(() {});
@ -819,11 +859,15 @@ class _MainAppState extends State<MainApp> {
: const SizedBox.shrink(), : const SizedBox.shrink(),
((Platform.isWindows || Platform.isLinux || Platform.isMacOS) && ((Platform.isWindows || Platform.isLinux || Platform.isMacOS) &&
MediaQuery.of(context).size.width >= 1000) MediaQuery.of(context).size.width >= 1000)
? VerticalDivider( ? AnimatedOpacity(
opacity: menuVisible ? 1.0 : 0.0,
duration: const Duration(milliseconds: 500),
child: VerticalDivider(
width: 2, width: 2,
color: (Theme.of(context).brightness == Brightness.light) color:
(Theme.of(context).brightness == Brightness.light)
? Colors.grey[400] ? Colors.grey[400]
: Colors.grey[900]) : Colors.grey[900]))
: const SizedBox.shrink(), : const SizedBox.shrink(),
Expanded( Expanded(
child: Chat( child: Chat(
@ -1021,6 +1065,7 @@ class _MainAppState extends State<MainApp> {
child: VisibilityDetector( child: VisibilityDetector(
key: const Key("logoVisible"), key: const Key("logoVisible"),
onVisibilityChanged: (VisibilityInfo info) { onVisibilityChanged: (VisibilityInfo info) {
if (settingsOpen) return;
logoVisible = info.visibleFraction > 0; logoVisible = info.visibleFraction > 0;
try { try {
setState(() {}); setState(() {});
@ -1515,7 +1560,11 @@ class _MainAppState extends State<MainApp> {
sendable = p0.trim().isNotEmpty; sendable = p0.trim().isNotEmpty;
}); });
}, },
sendButtonVisibilityMode: (sendable) sendButtonVisibilityMode: (Platform.isWindows ||
Platform.isLinux ||
Platform.isMacOS)
? SendButtonVisibilityMode.always
: (sendable)
? SendButtonVisibilityMode.always ? SendButtonVisibilityMode.always
: SendButtonVisibilityMode.hidden), : SendButtonVisibilityMode.hidden),
user: user, user: user,
@ -1585,6 +1634,10 @@ class _MainAppState extends State<MainApp> {
: 440))), : 440))),
], ],
), ),
drawerEdgeDragWidth:
(Platform.isWindows || Platform.isLinux || Platform.isMacOS)
? null
: MediaQuery.of(context).size.width,
drawer: Builder(builder: (context) { drawer: Builder(builder: (context) {
if ((Platform.isWindows || Platform.isLinux || Platform.isMacOS) && if ((Platform.isWindows || Platform.isLinux || Platform.isMacOS) &&
MediaQuery.of(context).size.width >= 1000) { MediaQuery.of(context).size.width >= 1000) {

View File

@ -16,6 +16,9 @@ import 'package:url_launcher/url_launcher.dart';
import 'package:restart_app/restart_app.dart'; import 'package:restart_app/restart_app.dart';
import 'package:file_picker/file_picker.dart'; import 'package:file_picker/file_picker.dart';
import 'package:intl/intl.dart'; import 'package:intl/intl.dart';
import 'package:install_referrer/install_referrer.dart';
import 'package:package_info_plus/package_info_plus.dart';
import 'package:version/version.dart';
class ScreenSettings extends StatefulWidget { class ScreenSettings extends StatefulWidget {
const ScreenSettings({super.key}); const ScreenSettings({super.key});
@ -68,7 +71,6 @@ class _ScreenSettingsState extends State<ScreenSettings> {
} }
if ((request.statusCode == 200 && request.body == "Ollama is running") || if ((request.statusCode == 200 && request.body == "Ollama is running") ||
(Uri.parse(tmpHost).toString() == fixedHost)) { (Uri.parse(tmpHost).toString() == fixedHost)) {
// messages = [];
setState(() { setState(() {
hostLoading = false; hostLoading = false;
host = tmpHost; host = tmpHost;
@ -91,10 +93,102 @@ class _ScreenSettingsState extends State<ScreenSettings> {
final repoUrl = "https://github.com/JHubi1/ollama-app"; final repoUrl = "https://github.com/JHubi1/ollama-app";
bool updateChecked = false;
bool updateLoading = false;
String updateStatus = "ok";
String? updateUrl;
String? latestVersion;
String? currentVersion;
Future<bool> updatesSupported([bool takeAction = false]) async {
bool returnValue = true;
var installerApps = [
"org.fdroid.fdroid",
"org.gdroid.gdroid",
"eu.bubu1.fdroidclassic",
"in.sunilpaulmathew.izzyondroid",
"com.looker.droidify",
"com.machiav3lli.fdroid",
"nya.kitsunyan.foxydroid"
];
if ((await InstallReferrer.referrer ==
InstallationAppReferrer.androidManually) &&
!(installerApps
.contains((await InstallReferrer.app).packageName ?? ""))) {
returnValue = false;
}
if (!repoUrl.startsWith("https://github.com")) {
returnValue = false;
}
if (!returnValue && takeAction) {
setState(() {
updateStatus = "notAvailable";
updateLoading = false;
});
}
return returnValue;
}
void checkUpdate() async {
setState(() {
updateChecked = true;
updateLoading = true;
});
if (!await updatesSupported()) {
setState(() {
updateStatus = "notAvailable";
updateLoading = false;
});
return;
}
var repo = repoUrl.split("/");
currentVersion = (await PackageInfo.fromPlatform()).version;
// currentVersion = "1.0.0";
String? version;
try {
var request = await http
.get(Uri.parse(
"https://api.github.com/repos/${repo[3]}/${repo[4]}/tags"))
.timeout(const Duration(seconds: 5));
if (request.statusCode == 403) {
setState(() {
updateStatus = "rateLimit";
updateLoading = false;
});
return;
}
version = jsonDecode(request.body)[0]["name"];
} catch (_) {
setState(() {
updateStatus = "error";
updateLoading = false;
});
return;
}
latestVersion = version;
updateUrl = "$repoUrl/releases/tag/$latestVersion";
updateStatus = "ok";
setState(() {
updateLoading = false;
});
}
@override @override
void initState() { void initState() {
super.initState(); super.initState();
WidgetsFlutterBinding.ensureInitialized();
checkHost(); checkHost();
updatesSupported(true);
if (prefs!.getBool("checkUpdateOnSettingsOpen") ?? false) {
checkUpdate();
}
} }
@override @override
@ -153,6 +247,7 @@ class _ScreenSettingsState extends State<ScreenSettings> {
return PopScope( return PopScope(
canPop: !hostLoading, canPop: !hostLoading,
onPopInvoked: (didPop) { onPopInvoked: (didPop) {
settingsOpen = false;
FocusManager.instance.primaryFocus?.unfocus(); FocusManager.instance.primaryFocus?.unfocus();
}, },
child: WindowBorder( child: WindowBorder(
@ -171,7 +266,6 @@ class _ScreenSettingsState extends State<ScreenSettings> {
child: WindowTitleBarBox( child: WindowTitleBarBox(
child: Row( child: Row(
children: [ children: [
// Expanded(child: MoveWindow()),
SizedBox( SizedBox(
height: 200, height: 200,
child: MinimizeWindowButton( child: MinimizeWindowButton(
@ -200,26 +294,7 @@ class _ScreenSettingsState extends State<ScreenSettings> {
))) )))
] ]
: null, : null,
leading: (Navigator.of(context).canPop()) ),
? null
: IconButton(
icon: const Icon(Icons.arrow_back),
onPressed: () {
if (hostLoading) return;
Navigator.of(context).pushReplacement(
// PageRouteBuilder(
// pageBuilder: (context, animation,
// secondaryAnimation) =>
// const MainApp(),
// // transitionDuration: const Duration(seconds: 1),
// transitionsBuilder: (context, animation,
// secondaryAnimation, child) {
// return FadeTransition(
// opacity: animation, child: child);
// })
MaterialPageRoute(
builder: (context) => const MainApp()));
})),
body: Padding( body: Padding(
padding: const EdgeInsets.only(left: 16, right: 16), padding: const EdgeInsets.only(left: 16, right: 16),
child: ListView(children: [ child: ListView(children: [
@ -619,6 +694,77 @@ class _ScreenSettingsState extends State<ScreenSettings> {
.settingsImportChats)) .settingsImportChats))
])), ])),
title(AppLocalizations.of(context)!.settingsTitleContact), title(AppLocalizations.of(context)!.settingsTitleContact),
(updateStatus == "notAvailable")
? const SizedBox.shrink()
: InkWell(
onTap: () {
if (updateLoading) return;
if ((Version.parse(latestVersion ?? "1.0.0") >
Version.parse(currentVersion ?? "2.0.0")) &&
(updateStatus == "ok")) {
launchUrl(
mode: LaunchMode.inAppBrowserView,
Uri.parse(updateUrl!));
} else {
checkUpdate();
return;
}
},
child: Row(children: [
updateLoading
? SizedBox(
width: 24,
height: 24,
child: Transform.scale(
scale: 0.5,
child:
const CircularProgressIndicator()),
)
: Icon((updateStatus != "ok")
? Icons.warning_rounded
: (Version.parse(latestVersion ?? "1.0.0") >
Version.parse(
currentVersion ?? "2.0.0"))
? Icons.info_outline_rounded
: Icons.update_rounded),
const SizedBox(width: 16, height: 42),
Expanded(
child: Text(!updateChecked
? AppLocalizations.of(context)!
.settingsUpdateCheck
: updateLoading
? AppLocalizations.of(context)!
.settingsUpdateChecking
: (updateStatus == "rateLimit")
? AppLocalizations.of(context)!
.settingsUpdateRateLimit
: (updateStatus != "ok")
? AppLocalizations.of(context)!
.settingsUpdateIssue
: (Version.parse(
latestVersion ??
"1.0.0") >
Version.parse(
currentVersion ??
"2.0.0"))
? AppLocalizations.of(
context)!
.settingsUpdateAvailable(
latestVersion!)
: AppLocalizations.of(
context)!
.settingsUpdateLatest))
])),
(updateStatus == "notAvailable")
? const SizedBox.shrink()
: toggle(
AppLocalizations.of(context)!.settingsCheckForUpdates,
(prefs!.getBool("checkUpdateOnSettingsOpen") ??
false), (value) {
HapticFeedback.selectionClick();
prefs!.setBool("checkUpdateOnSettingsOpen", value);
setState(() {});
}),
InkWell( InkWell(
onTap: () { onTap: () {
launchUrl( launchUrl(

View File

@ -64,10 +64,8 @@ void setModel(BuildContext context, Function setState) {
if (useModel) return; if (useModel) return;
HapticFeedback.selectionClick(); HapticFeedback.selectionClick();
showModalBottomSheet(
context: context, var content = StatefulBuilder(builder: (context, setLocalState) {
builder: (context) {
return StatefulBuilder(builder: (context, setLocalState) {
setModalState = setLocalState; setModalState = setLocalState;
return PopScope( return PopScope(
canPop: loaded, canPop: loaded,
@ -90,50 +88,65 @@ void setModel(BuildContext context, Function setState) {
setState(() {}); setState(() {});
}, },
child: Container( child: Container(
width: double.infinity, width:
((Platform.isWindows || Platform.isLinux || Platform.isMacOS) &&
MediaQuery.of(context).size.width >= 1000)
? null
: double.infinity,
padding: EdgeInsets.only( padding: EdgeInsets.only(
left: 16, left: 16,
right: 16, right: 16,
top: 16, top: 16,
bottom: (Platform.isWindows || bottom:
Platform.isLinux || (Platform.isWindows || Platform.isLinux || Platform.isMacOS)
Platform.isMacOS)
? 16 ? 16
: 0), : 0),
child: (!loaded) child: (!loaded)
? const LinearProgressIndicator() ? const LinearProgressIndicator()
: Column(mainAxisSize: MainAxisSize.min, children: [ : Column(mainAxisSize: MainAxisSize.min, children: [
Container( Container(
width: double.infinity, width: ((Platform.isWindows ||
Platform.isLinux ||
Platform.isMacOS) &&
MediaQuery.of(context).size.width >= 1000)
? 300
: double.infinity,
constraints: BoxConstraints( constraints: BoxConstraints(
maxHeight: maxHeight:
MediaQuery.of(context).size.height * 0.4), MediaQuery.of(context).size.height * 0.4),
child: SingleChildScrollView( child: SingleChildScrollView(
scrollDirection: Axis.vertical, scrollDirection: Axis.vertical,
child: Wrap( child: Wrap(
spacing: 5.0, spacing: ((Platform.isWindows ||
runSpacing: 5.0, Platform.isLinux ||
Platform.isMacOS) &&
MediaQuery.of(context).size.width >= 1000)
? 10.0
: 5.0,
runSpacing: (Platform.isWindows ||
Platform.isLinux ||
Platform.isMacOS)
? (MediaQuery.of(context).size.width >= 1000)
? 10.0
: 5.0
: 0.0,
alignment: WrapAlignment.center, alignment: WrapAlignment.center,
children: List<Widget>.generate( children: List<Widget>.generate(
models.length, models.length,
(int index) { (int index) {
return ChoiceChip( return ChoiceChip(
label: Text( label: Text(
(prefs!.getBool("modelTags") ?? (prefs!.getBool("modelTags") ?? false)
false)
? modelsReal[index] ? modelsReal[index]
: models[index]), : models[index]),
selected: usedIndex == index, selected: usedIndex == index,
avatar: (usedIndex == index) avatar: (usedIndex == index)
? null ? null
: (addIndex == index) : (addIndex == index)
? const Icon( ? const Icon(Icons.add_rounded)
Icons.add_rounded)
: ((recommendedModels : ((recommendedModels
.contains( .contains(models[index]))
models[index])) ? const Icon(Icons.star_rounded)
? const Icon(
Icons.star_rounded)
: ((modal[index]) : ((modal[index])
? const Icon(Icons ? const Icon(Icons
.collections_rounded) .collections_rounded)
@ -145,8 +158,7 @@ void setModel(BuildContext context, Function setState) {
? (theme ?? ThemeData()) ? (theme ?? ThemeData())
.colorScheme .colorScheme
.secondary .secondary
: (themeDark ?? : (themeDark ?? ThemeData.dark())
ThemeData.dark())
.colorScheme .colorScheme
.secondary) .secondary)
: null, : null,
@ -196,7 +208,17 @@ void setModel(BuildContext context, Function setState) {
))) )))
]))); ])));
}); });
if ((Platform.isWindows || Platform.isLinux || Platform.isMacOS) &&
MediaQuery.of(context).size.width >= 1000) {
showDialog(
context: context,
builder: (context) {
return Dialog(alignment: Alignment.topCenter, child: content);
}); });
} else {
showModalBottomSheet(context: context, builder: (context) => content);
}
} }
void saveChat(String uuid, Function setState) async { void saveChat(String uuid, Function setState) async {

View File

@ -421,6 +421,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.2.1+1" version: "0.2.1+1"
install_referrer:
dependency: "direct main"
description:
name: install_referrer
sha256: "901c56d24ee3c3010dfd0bbebf305ed6b4b0f3fe969192081c167590a64cd78b"
url: "https://pub.dev"
source: hosted
version: "1.2.1"
intl: intl:
dependency: "direct main" dependency: "direct main"
description: description:
@ -526,6 +534,22 @@ packages:
url: "https://github.com/davidmigloz/langchain_dart.git" url: "https://github.com/davidmigloz/langchain_dart.git"
source: git source: git
version: "0.1.0+1" version: "0.1.0+1"
package_info_plus:
dependency: "direct main"
description:
name: package_info_plus
sha256: b93d8b4d624b4ea19b0a5a208b2d6eff06004bc3ce74c06040b120eeadd00ce0
url: "https://pub.dev"
source: hosted
version: "8.0.0"
package_info_plus_platform_interface:
dependency: transitive
description:
name: package_info_plus_platform_interface
sha256: f49918f3433a3146047372f9d4f1f847511f2acd5cd030e1f44fe5a50036b70e
url: "https://pub.dev"
source: hosted
version: "3.0.0"
path: path:
dependency: transitive dependency: transitive
description: description:
@ -835,6 +859,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.1.4" version: "2.1.4"
version:
dependency: "direct main"
description:
name: version
sha256: "3d4140128e6ea10d83da32fef2fa4003fccbf6852217bb854845802f04191f94"
url: "https://pub.dev"
source: hosted
version: "3.0.2"
visibility_detector: visibility_detector:
dependency: "direct main" dependency: "direct main"
description: description:

View File

@ -9,22 +9,22 @@ environment:
dependencies: dependencies:
flutter: flutter:
sdk: flutter sdk: flutter
flutter_localizations:
sdk: flutter
ollama_dart:
git:
url: https://github.com/davidmigloz/langchain_dart.git
path: packages/ollama_dart
ref: ce2ef30c9a9a0dfe8f3059988b7007c94c45b9bd
intl: any
shared_preferences: ^2.2.3 shared_preferences: ^2.2.3
flutter_chat_ui: ^1.6.13 flutter_chat_ui: ^1.6.13
uuid: ^4.4.0 uuid: ^4.4.0
animated_text_kit: ^4.2.2 animated_text_kit: ^4.2.2
image_picker: ^1.1.1 image_picker: ^1.1.1
visibility_detector: ^0.4.0+2 visibility_detector: ^0.4.0+2
flutter_localizations:
sdk: flutter
intl: any
http: ^1.2.1 http: ^1.2.1
dartx: ^1.2.0 dartx: ^1.2.0
ollama_dart:
git:
url: https://github.com/davidmigloz/langchain_dart.git
path: packages/ollama_dart
ref: ce2ef30c9a9a0dfe8f3059988b7007c94c45b9bd
smooth_page_indicator: ^1.1.0 smooth_page_indicator: ^1.1.0
transparent_image: ^2.0.1 transparent_image: ^2.0.1
simple_icons: ^10.1.3 simple_icons: ^10.1.3
@ -33,6 +33,9 @@ dependencies:
flutter_markdown: ^0.7.1 flutter_markdown: ^0.7.1
file_picker: ^8.0.3 file_picker: ^8.0.3
bitsdojo_window: ^0.1.6 bitsdojo_window: ^0.1.6
install_referrer: ^1.2.1
package_info_plus: ^8.0.0
version: ^3.0.2
flutter_displaymode: ^0.6.0 flutter_displaymode: ^0.6.0
dev_dependencies: dev_dependencies: