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();
Navigator.of(context).pop(); if (!(Platform.isWindows ||
Platform.isLinux ||
Platform.isMacOS) &&
MediaQuery.of(context).size.width <= 1000) {
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();
Navigator.of(context).pop(); if (!(Platform.isWindows ||
Platform.isLinux ||
Platform.isMacOS) &&
MediaQuery.of(context).size.width <= 1000) {
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,7 +437,12 @@ class _MainAppState extends State<MainApp> {
if (chatUuid == jsonDecode(item)["uuid"]) { if (chatUuid == jsonDecode(item)["uuid"]) {
messages = []; messages = [];
chatUuid = null; chatUuid = null;
Navigator.of(context).pop(); if (!(Platform.isWindows ||
Platform.isLinux ||
Platform.isMacOS) &&
MediaQuery.of(context).size.width <= 1000) {
Navigator.of(context).pop();
}
} }
setState(() {}); setState(() {});
}, },
@ -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();
Navigator.of(context).pop(); if (!(Platform.isWindows ||
Platform.isLinux ||
Platform.isMacOS) &&
MediaQuery.of(context).size.width <= 1000) {
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,7 +813,20 @@ 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()
: const SizedBox.shrink()), : ((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()),
leading: ((Platform.isWindows || leading: ((Platform.isWindows ||
Platform.isLinux || Platform.isLinux ||
Platform.isMacOS) && Platform.isMacOS) &&
@ -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(
width: 2, opacity: menuVisible ? 1.0 : 0.0,
color: (Theme.of(context).brightness == Brightness.light) duration: const Duration(milliseconds: 500),
? Colors.grey[400] child: VerticalDivider(
: Colors.grey[900]) width: 2,
color:
(Theme.of(context).brightness == Brightness.light)
? Colors.grey[400]
: 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,9 +1560,13 @@ class _MainAppState extends State<MainApp> {
sendable = p0.trim().isNotEmpty; sendable = p0.trim().isNotEmpty;
}); });
}, },
sendButtonVisibilityMode: (sendable) sendButtonVisibilityMode: (Platform.isWindows ||
Platform.isLinux ||
Platform.isMacOS)
? SendButtonVisibilityMode.always ? SendButtonVisibilityMode.always
: SendButtonVisibilityMode.hidden), : (sendable)
? SendButtonVisibilityMode.always
: SendButtonVisibilityMode.hidden),
user: user, user: user,
hideBackgroundOnEmojiMessages: false, hideBackgroundOnEmojiMessages: false,
theme: (Theme.of(context).brightness == Brightness.light) theme: (Theme.of(context).brightness == Brightness.light)
@ -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,73 +247,54 @@ 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(
color: Theme.of(context).colorScheme.surface, color: Theme.of(context).colorScheme.surface,
child: Scaffold( child: Scaffold(
appBar: AppBar( appBar: AppBar(
title: Row(children: [ title: Row(children: [
Text(AppLocalizations.of(context)!.optionSettings), Text(AppLocalizations.of(context)!.optionSettings),
Expanded(child: SizedBox(height: 200, child: MoveWindow())) Expanded(child: SizedBox(height: 200, child: MoveWindow()))
]), ]),
actions: actions:
(Platform.isWindows || Platform.isLinux || Platform.isMacOS) (Platform.isWindows || Platform.isLinux || Platform.isMacOS)
? [ ? [
SizedBox( SizedBox(
height: 200, height: 200,
child: WindowTitleBarBox( child: WindowTitleBarBox(
child: Row( child: Row(
children: [ children: [
// Expanded(child: MoveWindow()), SizedBox(
SizedBox( height: 200,
height: 200, child: MinimizeWindowButton(
child: MinimizeWindowButton( animate: true,
animate: true, colors: WindowButtonColors(
colors: WindowButtonColors( iconNormal: Theme.of(context)
iconNormal: Theme.of(context) .colorScheme
.colorScheme .primary))),
.primary))), SizedBox(
SizedBox( height: 72,
height: 72, child: MaximizeWindowButton(
child: MaximizeWindowButton( animate: true,
animate: true, colors: WindowButtonColors(
colors: WindowButtonColors( iconNormal: Theme.of(context)
iconNormal: Theme.of(context) .colorScheme
.colorScheme .primary))),
.primary))), SizedBox(
SizedBox( height: 72,
height: 72, child: CloseWindowButton(
child: CloseWindowButton( animate: true,
animate: true, colors: WindowButtonColors(
colors: WindowButtonColors( iconNormal: Theme.of(context)
iconNormal: Theme.of(context) .colorScheme
.colorScheme .primary))),
.primary))), ],
], )))
))) ]
] : 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,139 +64,161 @@ 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) { setModalState = setLocalState;
return StatefulBuilder(builder: (context, setLocalState) { return PopScope(
setModalState = setLocalState; canPop: loaded,
return PopScope( onPopInvoked: (didPop) {
canPop: loaded, if (!loaded) return;
onPopInvoked: (didPop) { if (usedIndex >= 0 &&
if (!loaded) return; modelsReal[usedIndex] != model &&
if (usedIndex >= 0 && (prefs!.getBool("resetOnModelSelect") ?? true)) {
modelsReal[usedIndex] != model && messages = [];
(prefs!.getBool("resetOnModelSelect") ?? true)) { }
messages = []; model = (usedIndex >= 0) ? modelsReal[usedIndex] : null;
} chatAllowed = !(model == null);
model = (usedIndex >= 0) ? modelsReal[usedIndex] : null; multimodal = (usedIndex >= 0) ? modal[usedIndex] : false;
chatAllowed = !(model == null); if (model != null) {
multimodal = (usedIndex >= 0) ? modal[usedIndex] : false; prefs?.setString("model", model!);
if (model != null) { } else {
prefs?.setString("model", model!); prefs?.remove("model");
} else { }
prefs?.remove("model"); prefs?.setBool("multimodal", multimodal);
} setState(() {});
prefs?.setBool("multimodal", multimodal); },
setState(() {}); child: Container(
}, width:
child: Container( ((Platform.isWindows || Platform.isLinux || Platform.isMacOS) &&
width: double.infinity, MediaQuery.of(context).size.width >= 1000)
padding: EdgeInsets.only( ? null
left: 16, : double.infinity,
right: 16, padding: EdgeInsets.only(
top: 16, left: 16,
bottom: (Platform.isWindows || right: 16,
Platform.isLinux || top: 16,
Platform.isMacOS) bottom:
? 16 (Platform.isWindows || Platform.isLinux || Platform.isMacOS)
: 0), ? 16
child: (!loaded) : 0),
? const LinearProgressIndicator() child: (!loaded)
: Column(mainAxisSize: MainAxisSize.min, children: [ ? const LinearProgressIndicator()
Container( : Column(mainAxisSize: MainAxisSize.min, children: [
width: double.infinity, Container(
constraints: BoxConstraints( width: ((Platform.isWindows ||
maxHeight: Platform.isLinux ||
MediaQuery.of(context).size.height * 0.4), Platform.isMacOS) &&
child: SingleChildScrollView( MediaQuery.of(context).size.width >= 1000)
scrollDirection: Axis.vertical, ? 300
child: Wrap( : double.infinity,
spacing: 5.0, constraints: BoxConstraints(
runSpacing: 5.0, maxHeight:
alignment: WrapAlignment.center, MediaQuery.of(context).size.height * 0.4),
children: List<Widget>.generate( child: SingleChildScrollView(
models.length, scrollDirection: Axis.vertical,
(int index) { child: Wrap(
return ChoiceChip( spacing: ((Platform.isWindows ||
label: Text( Platform.isLinux ||
(prefs!.getBool("modelTags") ?? Platform.isMacOS) &&
false) MediaQuery.of(context).size.width >= 1000)
? modelsReal[index] ? 10.0
: models[index]), : 5.0,
selected: usedIndex == index, runSpacing: (Platform.isWindows ||
avatar: (usedIndex == index) Platform.isLinux ||
? null Platform.isMacOS)
: (addIndex == index) ? (MediaQuery.of(context).size.width >= 1000)
? const Icon( ? 10.0
Icons.add_rounded) : 5.0
: ((recommendedModels : 0.0,
.contains( alignment: WrapAlignment.center,
models[index])) children: List<Widget>.generate(
? const Icon( models.length,
Icons.star_rounded) (int index) {
: ((modal[index]) return ChoiceChip(
? const Icon(Icons label: Text(
.collections_rounded) (prefs!.getBool("modelTags") ?? false)
: null)), ? modelsReal[index]
checkmarkColor: (usedIndex == index) : models[index]),
? ((MediaQuery.of(context) selected: usedIndex == index,
.platformBrightness == avatar: (usedIndex == index)
Brightness.light) ? null
? (theme ?? ThemeData()) : (addIndex == index)
.colorScheme ? const Icon(Icons.add_rounded)
.secondary : ((recommendedModels
: (themeDark ?? .contains(models[index]))
ThemeData.dark()) ? const Icon(Icons.star_rounded)
.colorScheme : ((modal[index])
.secondary) ? const Icon(Icons
: null, .collections_rounded)
labelStyle: (usedIndex == index) : null)),
? TextStyle( checkmarkColor: (usedIndex == index)
color: (MediaQuery.of(context) ? ((MediaQuery.of(context)
.platformBrightness == .platformBrightness ==
Brightness.light) Brightness.light)
? (theme ?? ThemeData()) ? (theme ?? ThemeData())
.colorScheme .colorScheme
.secondary .secondary
: (themeDark ?? : (themeDark ?? ThemeData.dark())
ThemeData.dark()) .colorScheme
.colorScheme .secondary)
.secondary) : null,
: null, labelStyle: (usedIndex == index)
selectedColor: (MediaQuery.of(context) ? TextStyle(
.platformBrightness == color: (MediaQuery.of(context)
Brightness.light) .platformBrightness ==
? (theme ?? ThemeData()) Brightness.light)
.colorScheme ? (theme ?? ThemeData())
.primary .colorScheme
: (themeDark ?? ThemeData.dark()) .secondary
.colorScheme : (themeDark ??
.primary, ThemeData.dark())
onSelected: (bool selected) { .colorScheme
if (addIndex == index) { .secondary)
Navigator.of(context).pop(); : null,
ScaffoldMessenger.of(context) selectedColor: (MediaQuery.of(context)
.showSnackBar(SnackBar( .platformBrightness ==
content: Text( Brightness.light)
AppLocalizations.of( ? (theme ?? ThemeData())
context)! .colorScheme
.modelDialogAddSteps), .primary
showCloseIcon: true)); : (themeDark ?? ThemeData.dark())
} .colorScheme
if (!chatAllowed && model != null) { .primary,
return; onSelected: (bool selected) {
} if (addIndex == index) {
setLocalState(() { Navigator.of(context).pop();
usedIndex = selected ? index : -1; ScaffoldMessenger.of(context)
}); .showSnackBar(SnackBar(
}, content: Text(
); AppLocalizations.of(
}, context)!
).toList(), .modelDialogAddSteps),
))) showCloseIcon: true));
]))); }
if (!chatAllowed && model != null) {
return;
}
setLocalState(() {
usedIndex = selected ? index : -1;
});
},
);
},
).toList(),
)))
])));
});
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: