33 Commits

Author SHA1 Message Date
fd4c34c59d Merge pull request 'linux flatpak config pt5' (#25) from V.1.2.6 into main
Reviewed-on: #25
2026-02-25 13:50:19 +00:00
5e003a4d71 linux flatpak config pt5 2026-02-25 15:50:02 +02:00
dca595bf60 Merge pull request 'linux flatpak config pt4' (#24) from V.1.2.6 into main
Reviewed-on: #24
2026-02-25 13:44:37 +00:00
fcf1bbbb15 linux flatpak config pt4 2026-02-25 15:44:10 +02:00
8a0c0d7dba Merge pull request 'linux flatpak config pt3' (#23) from V.1.2.6 into main
Reviewed-on: #23
2026-02-25 13:36:22 +00:00
ff7f363983 linux flatpak config pt3 2026-02-25 15:35:45 +02:00
a54f1c61ae Merge pull request 'linux flatpak config pt2' (#22) from V.1.2.6 into main
Reviewed-on: #22
2026-02-25 13:10:05 +00:00
843997e58c linux flatpak config pt2 2026-02-25 15:09:23 +02:00
cbe70e2e44 Merge pull request 'linux flatpak config' (#21) from V.1.2.6 into main
Reviewed-on: #21
2026-02-25 12:52:15 +00:00
3778ebb261 linux flatpak config 2026-02-25 14:51:15 +02:00
c911e88453 Merge pull request 'linux name change' (#20) from V.1.2.6 into main
Reviewed-on: #20
2026-02-25 11:59:39 +00:00
b1487839a7 linux name change 2026-02-25 13:58:42 +02:00
518dd1300e Merge pull request 'fix platform specific code not working on web pt2' (#19) from V.1.2.6 into main
Reviewed-on: #19
2026-02-25 10:19:05 +00:00
221030eff3 fix platform specific code not working on web pt2 2026-02-25 12:16:53 +02:00
707b49c088 Merge pull request 'fix platform specific code not working on web' (#18) from V.1.2.6 into main
Reviewed-on: #18
2026-02-25 10:05:42 +00:00
5135629b33 fix platform specific code not working on web 2026-02-25 12:05:07 +02:00
bb64be077f Merge pull request 'api cros fix' (#17) from V.1.2.6 into main
Reviewed-on: #17
2026-02-25 09:55:54 +00:00
281ea863e8 api cros fix 2026-02-25 11:54:49 +02:00
64af64f28b Merge pull request 'V.1.2.6' (#16) from V.1.2.6 into main
Reviewed-on: #16
2026-02-25 09:30:49 +00:00
1c0dd6d328 update build number to 130 2026-02-25 10:25:28 +02:00
07d4ba4afa Support linux version of MIHpt2 2026-02-24 16:30:06 +02:00
6ad6b6ccbd Support linux version of MIH 2026-02-24 15:41:55 +02:00
ce2575035f make profile picture the full height of the window 2026-02-24 12:43:34 +02:00
baea2c9fdb fix file display on Dev Web & iOS 2026-02-24 12:37:31 +02:00
27639cb964 add scroll bar to dropdown fields 2026-02-24 11:05:24 +02:00
1143d11054 fix supertoken versioning error 2026-02-24 11:02:30 +02:00
213f3d418d update build number to 129 2026-02-18 15:41:37 +02:00
e33a62b909 update look & feel of attribution list 2026-02-18 14:03:04 +02:00
ebab9bae52 update build no to 128 2026-02-18 11:52:30 +02:00
82c25c5406 make profile picture expandable 2026-02-18 11:51:44 +02:00
3f0fc08a5c update business user edit workflow 2026-02-18 10:56:34 +02:00
f137ea41ac fix loading indicator alignment for business QR code 2026-02-18 10:41:49 +02:00
a7effa3576 Update profile link and business card Icons alignment 2026-02-18 10:26:41 +02:00
49 changed files with 873 additions and 713 deletions

View File

@@ -48,13 +48,15 @@ origins = [
"http://MIH-API-Hub",
"http://api.mzansi-innovation-hub.co.za",
"http://app.mzansi-innovation-hub.co.za",
"https://api.mzansi-innovation-hub.co.za",
"https://app.mzansi-innovation-hub.co.za",
]
init(
app_info=InputAppInfo(
app_name="Mzansi Innovation Hub",
api_domain="http://localhost:8080/",
website_domain="http://app.mzansi-innovation-hub.co.za",
website_domain="https://app.mzansi-innovation-hub.co.za",
api_base_path="/auth",
website_base_path="/auth"
),

View File

@@ -9,5 +9,5 @@ watchfiles
python-multipart
python-dotenv
xlrd
supertokens-python
supertokens-python==0.24.0
sniffio

View File

@@ -26,7 +26,15 @@ abstract class AppEnviroment {
baseApiUrl = "http://localhost:8080";
baseFileUrl = "http://localhost:9000";
baseAiUrl = "http://localhost:11434";
bannerAdUnitId = 'ca-app-pub-3940256099942544/2435281174';
bannerAdUnitId = 'ca-app-pub-3940256099942544/2435281174'; // IOS ID
break;
} else if (Platform.isIOS || Platform.isLinux) {
//================= Web Dev Urls =================
baseAppUrl = "http://localhost:80";
baseApiUrl = "http://localhost:8080";
baseFileUrl = "http://localhost:9000";
baseAiUrl = "http://localhost:11434";
bannerAdUnitId = 'ca-app-pub-3940256099942544/2435281174'; // IOS ID
break;
} else if (Platform.isAndroid) {
//================= Android Dev Urls =================
@@ -35,14 +43,6 @@ abstract class AppEnviroment {
baseFileUrl = "http://10.0.2.2:9000";
baseAiUrl = "http://10.0.2.2:11434";
bannerAdUnitId = 'ca-app-pub-3940256099942544/9214589741';
} else {
//================= Web & iOS Dev Urls =================
baseAppUrl = "http://localhost:80";
baseApiUrl = "http://localhost:8080";
baseFileUrl = "http://localhost:9000";
baseAiUrl = "http://localhost:11434";
bannerAdUnitId = 'ca-app-pub-3940256099942544/2435281174';
break;
}
}
case Enviroment.prod:

View File

@@ -98,6 +98,12 @@ class MihTheme {
return "Android";
} else if (platform == TargetPlatform.iOS) {
return "iOS";
} else if (platform == TargetPlatform.linux) {
return "Linux";
} else if (platform == TargetPlatform.macOS) {
return "macOS";
} else if (platform == TargetPlatform.windows) {
return "Windows";
}
}
return "Other";

View File

@@ -1,5 +1,8 @@
import 'dart:io';
import 'package:country_code_picker/country_code_picker.dart';
import 'package:file_picker/file_picker.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_speed_dial/flutter_speed_dial.dart';
import 'package:geolocator/geolocator.dart';
@@ -451,6 +454,7 @@ class _PackageToolOneState extends State<PackageToolOne> {
),
],
),
if (!kIsWeb && (Platform.isAndroid || Platform.isIOS))
MihBannerAd(),
const SizedBox(height: 10),
Divider(
@@ -774,6 +778,7 @@ class _PackageToolOneState extends State<PackageToolOne> {
MihCircleAvatar(
imageFile: imagePreview,
width: 50,
expandable: true,
editable: false,
fileNameController: _fileNameController,
userSelectedfile: file,

View File

@@ -186,7 +186,7 @@ class _MihBusinessCardV2State extends State<MihBusinessCardV2> {
return Consumer2<MzansiProfileProvider, MzansiDirectoryProvider>(
builder: (BuildContext context, MzansiProfileProvider profileProvider,
MzansiDirectoryProvider directoryProvider, Widget? child) {
double iconSize = 33.0;
double iconSize = 50.0;
return Wrap(
alignment: WrapAlignment.center,
runSpacing: 10,
@@ -194,10 +194,9 @@ class _MihBusinessCardV2State extends State<MihBusinessCardV2> {
children: [
Column(
children: [
SizedBox(
MihButton(
width: 80,
height: 80,
child: MihButton(
onPressed: () {
_makePhoneCall(widget.business.contact_no);
},
@@ -206,12 +205,10 @@ class _MihBusinessCardV2State extends State<MihBusinessCardV2> {
child: Icon(
Icons.phone,
color: MihColors.getPrimaryColor(
MzansiInnovationHub.of(context)!.theme.mode ==
"Dark"),
MzansiInnovationHub.of(context)!.theme.mode == "Dark"),
size: iconSize,
),
),
),
const SizedBox(height: 2),
FittedBox(
child: Text(
@@ -229,10 +226,9 @@ class _MihBusinessCardV2State extends State<MihBusinessCardV2> {
),
Column(
children: [
SizedBox(
MihButton(
width: 80,
height: 80,
child: MihButton(
onPressed: () {
_launchEmail(
widget.business.bus_email,
@@ -245,12 +241,10 @@ class _MihBusinessCardV2State extends State<MihBusinessCardV2> {
child: Icon(
Icons.email,
color: MihColors.getPrimaryColor(
MzansiInnovationHub.of(context)!.theme.mode ==
"Dark"),
MzansiInnovationHub.of(context)!.theme.mode == "Dark"),
size: iconSize,
),
),
),
const SizedBox(height: 2),
FittedBox(
child: Text(
@@ -269,10 +263,9 @@ class _MihBusinessCardV2State extends State<MihBusinessCardV2> {
if (isValidGps(widget.business.gps_location))
Column(
children: [
SizedBox(
MihButton(
width: 80,
height: 80,
child: MihButton(
onPressed: () {
final latitude = double.parse(
widget.business.gps_location.split(',')[0]);
@@ -284,8 +277,7 @@ class _MihBusinessCardV2State extends State<MihBusinessCardV2> {
);
},
buttonColor: MihColors.getOrangeColor(
MzansiInnovationHub.of(context)!.theme.mode ==
"Dark"),
MzansiInnovationHub.of(context)!.theme.mode == "Dark"),
child: Icon(
Icons.location_on,
color: MihColors.getPrimaryColor(
@@ -294,7 +286,6 @@ class _MihBusinessCardV2State extends State<MihBusinessCardV2> {
size: iconSize,
),
),
),
const SizedBox(height: 2),
FittedBox(
child: Text(
@@ -314,16 +305,14 @@ class _MihBusinessCardV2State extends State<MihBusinessCardV2> {
widget.business.website != "")
Column(
children: [
SizedBox(
MihButton(
width: 80,
height: 80,
child: MihButton(
onPressed: () {
_launchWebsite(widget.business.website);
},
buttonColor: MihColors.getRedColor(
MzansiInnovationHub.of(context)!.theme.mode ==
"Dark"),
MzansiInnovationHub.of(context)!.theme.mode == "Dark"),
child: Icon(
Icons.language,
color: MihColors.getPrimaryColor(
@@ -332,7 +321,6 @@ class _MihBusinessCardV2State extends State<MihBusinessCardV2> {
size: iconSize,
),
),
),
const SizedBox(height: 2),
FittedBox(
child: Text(
@@ -354,10 +342,9 @@ class _MihBusinessCardV2State extends State<MihBusinessCardV2> {
if (asyncSnapshot.connectionState == ConnectionState.waiting) {
return Column(
children: [
SizedBox(
MihButton(
width: 80,
height: 80,
child: MihButton(
onPressed: () {},
buttonColor: MihColors.getGreyColor(
MzansiInnovationHub.of(context)!.theme.mode ==
@@ -369,7 +356,6 @@ class _MihBusinessCardV2State extends State<MihBusinessCardV2> {
"Dark"),
size: iconSize,
),
),
).redacted(context: context, redact: true),
const SizedBox(height: 2),
FittedBox(
@@ -382,8 +368,8 @@ class _MihBusinessCardV2State extends State<MihBusinessCardV2> {
"Dark"),
fontSize: 20,
),
),
).redacted(context: context, redact: true),
),
],
);
} else {
@@ -396,10 +382,9 @@ class _MihBusinessCardV2State extends State<MihBusinessCardV2> {
}
return Column(
children: [
SizedBox(
MihButton(
width: 80,
height: 80,
child: MihButton(
onPressed: () {
businessReviewRatingWindow(directoryProvider,
businessReview, true, widget.width);
@@ -415,7 +400,6 @@ class _MihBusinessCardV2State extends State<MihBusinessCardV2> {
size: iconSize,
),
),
),
const SizedBox(height: 2),
FittedBox(
child: Text(
@@ -440,10 +424,9 @@ class _MihBusinessCardV2State extends State<MihBusinessCardV2> {
if (asyncSnapshot.connectionState == ConnectionState.waiting) {
return Column(
children: [
SizedBox(
MihButton(
width: 80,
height: 80,
child: MihButton(
onPressed: () {},
buttonColor: MihColors.getGreyColor(
MzansiInnovationHub.of(context)!.theme.mode ==
@@ -455,7 +438,6 @@ class _MihBusinessCardV2State extends State<MihBusinessCardV2> {
"Dark"),
size: iconSize,
),
),
).redacted(context: context, redact: true),
const SizedBox(height: 2),
FittedBox(
@@ -468,8 +450,8 @@ class _MihBusinessCardV2State extends State<MihBusinessCardV2> {
"Dark"),
fontSize: 20,
),
),
).redacted(context: context, redact: true),
),
],
);
} else {
@@ -482,10 +464,9 @@ class _MihBusinessCardV2State extends State<MihBusinessCardV2> {
}
return Column(
children: [
SizedBox(
MihButton(
width: 80,
height: 80,
child: MihButton(
onPressed: () {
if (bookmarkBusiness == null) {
showAddBookmarkAlert();
@@ -506,7 +487,6 @@ class _MihBusinessCardV2State extends State<MihBusinessCardV2> {
size: iconSize,
),
),
),
const SizedBox(height: 2),
FittedBox(
child: Text(

View File

@@ -68,6 +68,7 @@ class _MihBusinessProfilePreviewState extends State<MihBusinessProfilePreview> {
: MihCircleAvatar(
imageFile: widget.imageFile,
width: profilePictureWidth,
expandable: false,
editable: false,
fileNameController: TextEditingController(),
userSelectedfile: null,

View File

@@ -2,13 +2,17 @@ import 'dart:io';
import 'package:file_picker/file_picker.dart';
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:ken_logger/ken_logger.dart';
import 'package:mzansi_innovation_hub/main.dart';
import 'package:mzansi_innovation_hub/mih_package_components/mih_icons.dart';
import 'package:mzansi_innovation_hub/mih_config/mih_colors.dart';
import 'package:mzansi_innovation_hub/mih_package_components/mih_package_window.dart';
class MihCircleAvatar extends StatefulWidget {
final ImageProvider<Object>? imageFile;
final double width;
final bool expandable;
final bool editable;
final TextEditingController? fileNameController;
final onChange;
@@ -19,6 +23,7 @@ class MihCircleAvatar extends StatefulWidget {
super.key,
required this.imageFile,
required this.width,
required this.expandable,
required this.editable,
required this.fileNameController,
required this.userSelectedfile,
@@ -35,23 +40,33 @@ class _MihCircleAvatarState extends State<MihCircleAvatar> {
late ImageProvider<Object>? imagePreview;
ImageProvider<Object>? getAvatar() {
// Color dark = const Color(0XFF3A4454);
if (widget.imageFile == null) {
return null;
// if (widget.backgroundColor == dark) {
// print("here in light icon");
// return const AssetImage(
// 'lib/mih_package_components/assets/images/i-dont-know-light.png');
// } else {
// print("here in dark icon");
// return const AssetImage(
// 'lib/mih_package_components/assets/images/i-dont-know-dark.png');
// }
} else {
return widget.imageFile;
}
}
void expandAvatar() {
showDialog(
context: context,
builder: (context) {
return MihPackageWindow(
fullscreen: true,
windowTitle: "",
scrollbarOn: false,
onWindowTapClose: () {
context.pop();
},
windowBody: SizedBox.expand(
child: InteractiveViewer(
child: Image(image: imagePreview!),
),
),
);
});
}
@override
void initState() {
super.initState();
@@ -62,8 +77,14 @@ class _MihCircleAvatarState extends State<MihCircleAvatar> {
@override
Widget build(BuildContext context) {
return Container(
// color: Colors.white,
return GestureDetector(
onTap: widget.expandable
? () {
KenLogger.success("Avatar tapped");
expandAvatar();
}
: null,
child: Container(
alignment: Alignment.center,
width: widget.width,
height: widget.width,
@@ -106,7 +127,8 @@ class _MihCircleAvatarState extends State<MihCircleAvatar> {
style: ButtonStyle(
backgroundColor: WidgetStateProperty.all<Color>(
MihColors.getGreenColor(
MzansiInnovationHub.of(context)!.theme.mode == "Dark"),
MzansiInnovationHub.of(context)!.theme.mode ==
"Dark"),
),
),
onPressed: () async {
@@ -116,7 +138,9 @@ class _MihCircleAvatarState extends State<MihCircleAvatar> {
type: FileType.image,
);
// print("Here 1");
if (MzansiInnovationHub.of(context)!.theme.getPlatform() ==
if (MzansiInnovationHub.of(context)!
.theme
.getPlatform() ==
"Web") {
// print("Here 2");
if (result == null) return;
@@ -168,6 +192,7 @@ class _MihCircleAvatarState extends State<MihCircleAvatar> {
),
],
),
),
);
}
}

View File

@@ -103,6 +103,18 @@ class _MihDropdownFieldState extends State<MihDropdownField> {
Expanded(
child: Theme(
data: Theme.of(context).copyWith(
scrollbarTheme: ScrollbarThemeData(
thumbColor: WidgetStatePropertyAll(
MihColors.getPrimaryColor(
MzansiInnovationHub.of(context)!
.theme
.mode ==
"Dark")),
thickness: const WidgetStatePropertyAll(6),
radius: const Radius.circular(10),
thumbVisibility: const WidgetStatePropertyAll(
true), // Always show when scrolling
),
textSelectionTheme: TextSelectionThemeData(
cursorColor: MihColors.getPrimaryColor(
MzansiInnovationHub.of(context)!.theme.mode ==

View File

@@ -19,9 +19,6 @@ class _MihloadingcircleState extends State<Mihloadingcircle>
late AnimationController _controller;
late Animation<double> _animation;
late double width;
late double height;
@override
void initState() {
super.initState();
@@ -82,16 +79,15 @@ class _MihloadingcircleState extends State<Mihloadingcircle>
},
),
),
widget.message != null
? Text(
if (widget.message != null)
Text(
widget.message!,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
),
)
: SizedBox(),
),
],
)),
),

View File

@@ -1,7 +1,9 @@
import 'dart:io';
import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart';
import 'package:go_router/go_router.dart';
import 'package:ken_logger/ken_logger.dart';
import 'package:mzansi_innovation_hub/main.dart';
import 'package:mzansi_innovation_hub/mih_package_components/mih_scack_bar.dart';
import 'package:mzansi_innovation_hub/mih_packages/mih_home/components/mih_app_drawer.dart';
import 'package:mzansi_innovation_hub/mih_package_components/mih_package_tools.dart';
@@ -100,7 +102,7 @@ class _MihPackageState extends State<MihPackage>
// _peakAnimation();
// });
// }
if (!MzansiInnovationHub.of(context)!.theme.kIsWeb) {
if (!kIsWeb && (Platform.isAndroid || Platform.isIOS)) {
// Trigger the peak animation only AFTER the route transition is complete
WidgetsBinding.instance.addPostFrameCallback((_) {
final ModalRoute? currentRoute = ModalRoute.of(context);

View File

@@ -1,3 +1,5 @@
import 'dart:io';
import 'package:app_settings/app_settings.dart';
import 'package:flutter/foundation.dart';
import 'package:local_auth/local_auth.dart';
@@ -155,7 +157,8 @@ class _MihPackageTileState extends State<MihPackageTile> {
Future<void> authenticateUser() async {
if (widget.authenticateUser != null &&
widget.authenticateUser! &&
!kIsWeb) {
!kIsWeb &&
!Platform.isLinux) {
if (await isUserAuthenticated()) {
widget.onTap();
}

View File

@@ -15,6 +15,7 @@ class MihPackageWindow extends StatefulWidget {
final Color? foregroundColor;
final bool? borderOn;
final bool fullscreen;
final bool? scrollbarOn;
const MihPackageWindow({
super.key,
required this.fullscreen,
@@ -23,6 +24,7 @@ class MihPackageWindow extends StatefulWidget {
required this.onWindowTapClose,
required this.windowBody,
this.borderOn,
this.scrollbarOn,
this.backgroundColor,
this.foregroundColor,
});
@@ -177,7 +179,13 @@ class _MihPackageWindowState extends State<MihPackageWindow> {
getHeader(),
const SizedBox(height: 5),
Expanded(
child: SingleChildScrollView(child: widget.windowBody)),
child: widget.scrollbarOn != null || !widget.scrollbarOn!
? widget.windowBody
: MihSingleChildScroll(
scrollbarOn: true,
child: widget.windowBody,
),
),
],
)
: Column(

View File

@@ -49,6 +49,7 @@ class _MihPersonalProfilePreviewState extends State<MihPersonalProfilePreview> {
: MihCircleAvatar(
imageFile: widget.imageFile,
width: profilePictureWidth,
expandable: false,
editable: false,
fileNameController: TextEditingController(),
userSelectedfile: null,

View File

@@ -10,12 +10,10 @@ import 'package:url_launcher/url_launcher.dart';
class MihProfileLinks extends StatefulWidget {
final List<ProfileLink> links;
final double? buttonSize;
final bool? paddingOn;
const MihProfileLinks({
super.key,
required this.links,
this.buttonSize,
this.paddingOn,
});
@@ -83,6 +81,8 @@ class _MihProfileLinksState extends State<MihProfileLinks> {
MzansiInnovationHub.of(context)!.theme.mode == "Dark");
}
return MihButton(
width: 80,
height: 80,
onPressed: () {
launchSocialUrl(Uri.parse(link.web_link));
},
@@ -90,7 +90,7 @@ class _MihProfileLinksState extends State<MihProfileLinks> {
child: FaIcon(
iconData,
color: iconColor,
size: 33,
size: 40,
),
);
// return MihPackageTile(
@@ -148,11 +148,7 @@ class _MihProfileLinksState extends State<MihProfileLinks> {
spacing: 10,
children: widget.links.map(
(link) {
return SizedBox(
width: widget.buttonSize ?? 80,
height: widget.buttonSize ?? 80,
child: displayLinkButton(link),
);
return displayLinkButton(link);
},
).toList(),
),

View File

@@ -1,7 +1,6 @@
import 'package:flutter/material.dart';
import 'package:mzansi_innovation_hub/main.dart';
import 'package:mzansi_innovation_hub/mih_package_components/mih_single_child_scroll.dart';
import 'package:mzansi_innovation_hub/mih_package_components/mih_button.dart';
import 'package:mzansi_innovation_hub/mih_package_components/mih_package_tool_body.dart';
import 'package:mzansi_innovation_hub/mih_package_components/mih_icons.dart';
import 'package:mzansi_innovation_hub/mih_config/mih_colors.dart';
@@ -21,72 +20,30 @@ class _MihAttributesState extends State<MihAttributes> {
}
}
TableRow displayIcon(IconData icon, String creator, String link) {
return TableRow(
children: [
TableCell(
verticalAlignment: TableCellVerticalAlignment.middle,
child: SizedBox(
height: 150,
child: Padding(
padding: const EdgeInsets.only(bottom: 15.0),
child: FittedBox(
child: Center(
child: Icon(
icon,
// size: 125,
color: MihColors.getSecondaryColor(
MzansiInnovationHub.of(context)!.theme.mode == "Dark"),
),
),
),
),
),
),
TableCell(
verticalAlignment: TableCellVerticalAlignment.middle,
child: Padding(
padding: const EdgeInsets.all(15.0),
child: Center(
child: Text(
creator,
textAlign: TextAlign.center,
style: const TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
),
),
),
),
),
TableCell(
verticalAlignment: TableCellVerticalAlignment.middle,
child: Padding(
padding: const EdgeInsets.only(bottom: 15.0),
child: MihButton(
onPressed: () {
Widget displayAttribution(IconData resource, String creator, String link) {
return GestureDetector(
onTap: () {
launchUrlLink(
Uri.parse(
link,
),
);
},
buttonColor: MihColors.getGreenColor(
child: Column(
children: [
Icon(
resource,
color: MihColors.getSecondaryColor(
MzansiInnovationHub.of(context)!.theme.mode == "Dark"),
width: 100,
child: Text(
"Visit",
style: TextStyle(
color: MihColors.getPrimaryColor(
MzansiInnovationHub.of(context)!.theme.mode == "Dark"),
fontSize: 20,
fontWeight: FontWeight.bold,
),
),
),
size: 100,
),
const SizedBox(height: 5),
Text(
creator,
style: const TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
),
],
),
);
}
@@ -132,157 +89,50 @@ class _MihAttributesState extends State<MihAttributes> {
height: 10,
),
SizedBox(
width: 700,
child: Table(
defaultVerticalAlignment: TableCellVerticalAlignment.middle,
columnWidths: const {
0: FlexColumnWidth(1),
1: FlexColumnWidth(1),
2: FlexColumnWidth(1),
},
width: 900,
child: Wrap(
alignment: WrapAlignment.center,
runSpacing: 10,
spacing: 10,
children: [
const TableRow(
children: [
TableCell(
verticalAlignment: TableCellVerticalAlignment.middle,
child: Padding(
padding: const EdgeInsets.only(bottom: 15.0),
child: Center(
child: Text(
"Resources",
style: TextStyle(
fontSize: 25,
fontWeight: FontWeight.bold,
),
),
),
),
),
TableCell(
verticalAlignment: TableCellVerticalAlignment.middle,
child: Padding(
padding: const EdgeInsets.only(bottom: 15.0),
child: Center(
child: Text(
"Creator",
style: TextStyle(
fontSize: 25,
fontWeight: FontWeight.bold,
),
),
),
),
),
TableCell(
child: Padding(
padding: const EdgeInsets.only(bottom: 15.0),
child: Center(
child: Text(
"Link",
style: TextStyle(
fontSize: 25,
fontWeight: FontWeight.bold,
),
),
),
),
),
],
),
displayIcon(MihIcons.mihRing, "Tarah Meth",
displayAttribution(MihIcons.mihRing, "Tarah Meth",
"https://www.linkedin.com/in/tarah-meth-3b6309254/"),
displayIcon(MihIcons.mihLogo, "Tarah Meth",
displayAttribution(MihIcons.mihLogo, "Tarah Meth",
"https://www.linkedin.com/in/tarah-meth-3b6309254/"),
displayIcon(
displayAttribution(
MihIcons.mzansiAi, "Ollama", "https://ollama.com/"),
displayIcon(MihIcons.mzansiWallet, "Freepik",
displayAttribution(MihIcons.mzansiWallet, "Freepik",
"https://www.flaticon.com/free-icon/wallet-passes-app_3884407?term=wallet&page=1&position=21&origin=search&related_id=3884407"),
displayIcon(MihIcons.patientProfile, "RaftelDesign",
displayAttribution(MihIcons.patientProfile, "RaftelDesign",
"https://www.flaticon.com/free-icon/patient_2376100?term=medication&page=1&position=6&origin=search&related_id=2376100"),
displayIcon(MihIcons.patientProfile, "Srip",
displayAttribution(MihIcons.patientProfile, "Srip",
"https://www.flaticon.com/free-icon/hospital_1233930?term=medical+snake&page=1&position=7&origin=search&related_id=1233930"),
displayIcon(MihIcons.calendar, "Freepik",
displayAttribution(MihIcons.calendar, "Freepik",
"https://www.flaticon.com/free-icon/calendar_2278049?term=calendar&page=1&position=5&origin=search&related_id=2278049"),
displayIcon(MihIcons.calculator, "Freepik",
displayAttribution(MihIcons.calculator, "Freepik",
"https://www.flaticon.com/free-icon/calculator_2374409?term=calculator&page=1&position=20&origin=search&related_id=2374409"),
displayIcon(MihIcons.aboutMih, "Chanut",
displayAttribution(MihIcons.aboutMih, "Chanut",
"https://www.flaticon.com/free-icon/info_151776?term=about&page=1&position=8&origin=search&related_id=151776"),
displayIcon(MihIcons.personalProfile, "Freepik",
displayAttribution(MihIcons.personalProfile, "Freepik",
"https://www.flaticon.com/free-icon/user_1077063?term=profile&page=1&position=6&origin=search&related_id=1077063"),
displayIcon(MihIcons.businessProfile, "Gravisio",
displayAttribution(MihIcons.businessProfile, "Gravisio",
"https://www.flaticon.com/free-icon/contractor_11813336?term=company+profile&page=1&position=2&origin=search&related_id=11813336"),
displayIcon(MihIcons.patientManager, "Vector Tank",
displayAttribution(MihIcons.patientManager, "Vector Tank",
"https://www.flaticon.com/free-icon/doctor_10215061?term=doctor&page=1&position=73&origin=search&related_id=10215061"),
displayIcon(MihIcons.profileSetup, "Freepik",
displayAttribution(MihIcons.profileSetup, "Freepik",
"https://www.flaticon.com/free-icon/add-user_748137?term=profile+add&page=1&position=1&origin=search&related_id=748137"),
displayIcon(MihIcons.businessSetup, "kerismaker",
displayAttribution(MihIcons.businessSetup, "kerismaker",
"https://www.flaticon.com/free-icon/business_13569850?term=company+add&page=1&position=25&origin=search&related_id=13569850"),
displayIcon(MihIcons.calculator, "fawazahmed0",
displayAttribution(MihIcons.calculator, "fawazahmed0",
"https://github.com/fawazahmed0/exchange-api"),
displayIcon(MihIcons.iDontKnow, "Freepik",
displayAttribution(MihIcons.iDontKnow, "Freepik",
"https://www.flaticon.com/free-icon/i-dont-know_5359909?term=i+dont+know&page=1&position=7&origin=search&related_id=5359909"),
],
),
),
// SizedBox(
// width: 500,
// child: Column(
// children: [
// const SizedBox(
// width: double.infinity,
// child: Row(
// mainAxisAlignment: MainAxisAlignment.spaceEvenly,
// mainAxisSize: MainAxisSize.max,
// children: [
// Flexible(
// child: Text(
// "Icon",
// style: TextStyle(
// fontSize: 25,
// fontWeight: FontWeight.bold,
// ),
// ),
// ),
// Flexible(
// child: Text(
// "Creator",
// style: TextStyle(
// fontSize: 25,
// fontWeight: FontWeight.bold,
// ),
// ),
// ),
// Flexible(
// child: Text(
// "Link",
// style: TextStyle(
// fontSize: 25,
// fontWeight: FontWeight.bold,
// ),
// ),
// ),
// ],
// ),
// ),
// const Padding(
// padding: EdgeInsets.symmetric(vertical: 10.0),
// child: Divider(),
// ),
// displayIcon(MihIcons.mihLogo, "Tarah Meth",
// "https://app.mzansi-innovation-hub.co.za/"),
// const SizedBox(height: 10),
// displayIcon(MihIcons.mihLogo, "Test",
// "https://www.flaticon.com/free-icons/mih"),
// const SizedBox(height: 10),
// displayIcon(MihIcons.mihLogo, "Test",
// "https://www.flaticon.com/free-icons/mih"),
// const SizedBox(height: 10),
// displayIcon(MihIcons.mihLogo, "Test",
// "https://www.flaticon.com/free-icons/mih"),
// const SizedBox(height: 10),
// ],
// ),
// )
const SizedBox(
height: 30,
),
],
),
),

View File

@@ -1,3 +1,6 @@
import 'dart:io';
import 'package:flutter/foundation.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:mzansi_innovation_hub/main.dart';
@@ -157,7 +160,11 @@ class _CurrencyExchangeRateState extends State<CurrencyExchangeRate> {
),
SizedBox(height: 10),
Consumer(builder: (context, bannerAdDisplay, child) {
if (!kIsWeb && (Platform.isAndroid || Platform.isIOS)) {
return MihBannerAd();
} else {
return const SizedBox(height: 0);
}
}),
],
),

View File

@@ -1,3 +1,6 @@
import 'dart:io';
import 'package:flutter/foundation.dart';
import 'package:mzansi_innovation_hub/main.dart';
import 'package:mzansi_innovation_hub/mih_package_components/mih_banner_ad.dart';
import 'package:mzansi_innovation_hub/mih_config/mih_colors.dart';
@@ -229,7 +232,11 @@ class _TipCalcState extends State<TipCalc> {
),
SizedBox(height: 10),
Consumer(builder: (context, bannerAdDisplay, child) {
if (!kIsWeb && (Platform.isAndroid || Platform.isIOS)) {
return MihBannerAd();
} else {
return const SizedBox(height: 0);
}
}),
// if (splitBillController.text == "Yes") const Divider(),
],

View File

@@ -78,6 +78,7 @@ class _MIHAppDrawerState extends State<MIHAppDrawer> {
? mzansiProfileProvider.userProfilePicture
: mzansiProfileProvider.businessProfilePicture,
width: 60,
expandable: false,
editable: false,
fileNameController: proPicController,
onChange: (_) {},

View File

@@ -353,6 +353,7 @@ class _MihHomeState extends State<MihHome> {
key: Key(imageKey),
imageFile: currentImage,
width: 50,
expandable: false,
editable: false,
fileNameController: null,
userSelectedfile: null,

View File

@@ -104,6 +104,7 @@ class _BuildMinesweeperLeaderboardListState
key: UniqueKey(),
imageFile: imageFile,
width: 80,
expandable: true,
editable: false,
fileNameController: null,
userSelectedfile: null,

View File

@@ -50,6 +50,7 @@ class LeaderboardUserRanking extends StatelessWidget {
.toString()), // Use ValueKey for stable identity
imageFile: asyncSnapshot.data,
width: 60,
expandable: true,
editable: false,
fileNameController: null,
userSelectedfile: null,

View File

@@ -1,5 +1,7 @@
import 'dart:async';
import 'dart:io';
import 'dart:math';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_speed_dial/flutter_speed_dial.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
@@ -851,7 +853,9 @@ class _MineSweeperGameState extends State<MineSweeperGame> {
],
),
),
_timer != null ? MihBannerAd() : SizedBox(),
_timer != null && !kIsWeb && (Platform.isAndroid || Platform.isIOS)
? MihBannerAd()
: SizedBox(),
SizedBox(height: 15),
],
);

View File

@@ -87,6 +87,7 @@ class _MihMineSweeperLeaderBoardState extends State<MyScoreBoard> {
child: MihCircleAvatar(
imageFile: profileProvider.userProfilePicture,
width: 150,
expandable: true,
editable: false,
fileNameController: null,
userSelectedfile: null,

View File

@@ -1,4 +1,5 @@
import 'dart:convert';
import 'dart:io';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
@@ -112,16 +113,36 @@ class _MihAiChatState extends State<MihAiChat> with WidgetsBindingObserver {
void speakLastMessage(MzansiAiProvider aiProvider) {
final history = aiProvider.ollamaProvider.history;
if (history.isNotEmpty) {
if (history.isEmpty) return;
final historyList = history.toList();
String? textToSpeak;
// Find the last LLM message
for (int i = historyList.length - 1; i >= 0; i--) {
if (historyList[i].origin == MessageOrigin.llm &&
historyList[i].text != null &&
historyList[i].text!.isNotEmpty) {
_flutterTts.speak(historyList[i].text!);
return;
textToSpeak = historyList[i].text!;
break;
}
}
if (textToSpeak != null) {
if (!kIsWeb && Platform.isLinux) {
// Linux Workaround: Use Speech Dispatcher (standard on most distros)
// '-t female1' is optional for voice variety
Process.run('spd-say', [textToSpeak]);
// Since spd-say doesn't have an easy "completion handler" via CLI,
// we manually toggle the UI state or just leave it off.
aiProvider.setTTSstate(true);
Future.delayed(
Duration(seconds: 5), () => aiProvider.setTTSstate(false));
} else {
// Your existing mobile/web logic
_flutterTts.speak(textToSpeak);
}
}
}
@@ -183,11 +204,16 @@ class _MihAiChatState extends State<MihAiChat> with WidgetsBindingObserver {
// }
void stopTTS(MzansiAiProvider aiProvider) {
if (!kIsWeb && Platform.isLinux) {
Process.run('spd-say', ['-S']); // The -S flag stops current speech
} else {
_flutterTts.stop();
}
aiProvider.setTTSstate(false);
}
Future<void> initTts(MzansiAiProvider aiProvider) async {
if (!kIsWeb && Platform.isLinux) return;
try {
await _flutterTts.setSpeechRate(!kIsWeb ? 0.55 : 1);
// await _flutterTts.setLanguage("en-US");
@@ -258,7 +284,9 @@ class _MihAiChatState extends State<MihAiChat> with WidgetsBindingObserver {
@override
void dispose() {
if (!kIsWeb && !Platform.isLinux) {
_flutterTts.stop();
}
WidgetsBinding.instance.removeObserver(this);
super.dispose();
}

View File

@@ -271,6 +271,7 @@ class _MihUpdateBusinessDetailsWindowState
: mzansiProfileProvider
.businessProfilePicture,
width: 150,
expandable: false,
editable: true,
fileNameController: fileNameController,
userSelectedfile: newSelectedLogoPic,

View File

@@ -5,7 +5,6 @@ import 'package:mzansi_innovation_hub/main.dart';
import 'package:mzansi_innovation_hub/mih_config/mih_colors.dart';
import 'package:mzansi_innovation_hub/mih_config/mih_env.dart';
import 'package:mzansi_innovation_hub/mih_package_components/mih_button.dart';
import 'package:mzansi_innovation_hub/mih_package_components/mih_circle_avatar.dart';
import 'package:mzansi_innovation_hub/mih_package_components/mih_form.dart';
import 'package:mzansi_innovation_hub/mih_package_components/mih_image_display.dart';
import 'package:mzansi_innovation_hub/mih_package_components/mih_package_window.dart';
@@ -133,27 +132,29 @@ class _MihUpdateMyBusinessUserDetailsState
MzansiInnovationHub.of(context)!.theme.screenType == "desktop"
? EdgeInsets.symmetric(horizontal: width * 0.2)
: EdgeInsets.symmetric(horizontal: width * 0.075),
child: Column(
child: Stack(
children: [
Column(
children: [
MihForm(
formKey: _formKey,
formFields: [
Center(
child: MihCircleAvatar(
imageFile: mzansiProfileProvider.userProfilePicture,
width: 150,
editable: false,
fileNameController: fileNameController,
userSelectedfile: userPicFile,
frameColor: MihColors.getSecondaryColor(
MzansiInnovationHub.of(context)!.theme.mode ==
"Dark"),
backgroundColor: MihColors.getPrimaryColor(
MzansiInnovationHub.of(context)!.theme.mode ==
"Dark"),
onChange: (_) {},
),
),
// Center(
// child: MihCircleAvatar(
// imageFile: mzansiProfileProvider.userProfilePicture,
// width: 150,
// editable: false,
// fileNameController: fileNameController,
// userSelectedfile: userPicFile,
// frameColor: MihColors.getSecondaryColor(
// MzansiInnovationHub.of(context)!.theme.mode ==
// "Dark"),
// backgroundColor: MihColors.getPrimaryColor(
// MzansiInnovationHub.of(context)!.theme.mode ==
// "Dark"),
// onChange: (_) {},
// ),
// ),
Visibility(
visible: false,
child: MihTextFormField(
@@ -302,7 +303,9 @@ class _MihUpdateMyBusinessUserDetailsState
"Update",
style: TextStyle(
color: MihColors.getPrimaryColor(
MzansiInnovationHub.of(context)!.theme.mode ==
MzansiInnovationHub.of(context)!
.theme
.mode ==
"Dark"),
fontSize: 20,
fontWeight: FontWeight.bold,
@@ -315,6 +318,35 @@ class _MihUpdateMyBusinessUserDetailsState
),
],
),
Positioned(
top: 0,
right: 0,
child: MihButton(
onPressed: () {
if (_formKey.currentState!.validate()) {
submitForm(mzansiProfileProvider);
} else {
MihAlertServices().inputErrorAlert(context);
}
},
buttonColor: MihColors.getGreenColor(
MzansiInnovationHub.of(context)!.theme.mode == "Dark"),
width: 100,
height: 25,
child: Text(
"Update",
style: TextStyle(
color: MihColors.getPrimaryColor(
MzansiInnovationHub.of(context)!.theme.mode ==
"Dark"),
fontSize: 15,
fontWeight: FontWeight.bold,
),
),
),
),
],
),
),
);
},

View File

@@ -77,6 +77,7 @@ class _MihBusinessDetailsState extends State<MihBusinessDetails> {
imageFile:
mzansiProfileProvider.businessProfilePicture,
width: 150,
expandable: true,
editable: false,
fileNameController: fileNameController,
userSelectedfile: newSelectedLogoPic,

View File

@@ -330,6 +330,7 @@ class _MihBusinessDetailsSetUpState extends State<MihBusinessDetailsSetUp> {
? MemoryImage(newSelectedLogoPic!.bytes!)
: mzansiProfileProvider.businessProfilePicture,
width: 150,
expandable: false,
editable: true,
fileNameController: logoFileNameController,
userSelectedfile: newSelectedLogoPic,

View File

@@ -77,6 +77,7 @@ class _MihBusinessDetailsViewState extends State<MihBusinessDetailsView> {
imageFile: CachedNetworkImageProvider(
asyncSnapshot.requireData),
width: profilePictureWidth,
expandable: true,
editable: false,
fileNameController: TextEditingController(),
userSelectedfile: file,

View File

@@ -1,3 +1,5 @@
import 'dart:io';
import 'package:cached_network_image/cached_network_image.dart';
import 'package:file_picker/file_picker.dart';
import 'package:file_saver/file_saver.dart';
@@ -82,6 +84,19 @@ class _MihBusinessQrCodeState extends State<MihBusinessQrCode> {
fileExtension: "png",
mimeType: MimeType.png,
);
} else if (defaultTargetPlatform == TargetPlatform.linux ||
defaultTargetPlatform == TargetPlatform.windows) {
// Use File Picker to get a save path on Desktop
String? outputFile = await FilePicker.platform.saveFile(
dialogTitle: 'Please select where to save your QR Code:',
fileName: filename,
);
if (outputFile != null) {
final file = File(outputFile);
await file.writeAsBytes(imageBytes);
KenLogger.success("Saved to $outputFile");
}
} else {
await FileSaver.instance.saveAs(
name: filename,
@@ -95,14 +110,14 @@ class _MihBusinessQrCodeState extends State<MihBusinessQrCode> {
Future<void> downloadQrCode() async {
if (_isUserSignedIn) {
await screenshotController.capture().then((image) {
KenLogger.success("Image Captured: $image");
// KenLogger.success("Image Captured: $image");
setState(() {
businessQRImageFile = image;
});
}).catchError((onError) {
KenLogger.error(onError);
});
KenLogger.success("QR Code Image Captured : $businessQRImageFile");
// KenLogger.success("QR Code Image Captured : $businessQRImageFile");
saveImage(businessQRImageFile!);
} else {
showSignInRequiredAlert();
@@ -211,6 +226,7 @@ class _MihBusinessQrCodeState extends State<MihBusinessQrCode> {
imageFile: CachedNetworkImageProvider(
asyncSnapshot.requireData),
width: profilePictureWidth,
expandable: true,
editable: false,
fileNameController: TextEditingController(),
userSelectedfile: file,
@@ -298,7 +314,9 @@ class _MihBusinessQrCodeState extends State<MihBusinessQrCode> {
height: 300,
child: CachedNetworkImage(
imageUrl: getQrCodeData(qrSize.toInt()),
placeholder: (context, url) => const Mihloadingcircle(),
placeholder: (context, url) => FittedBox(
child: const Mihloadingcircle(),
),
errorWidget: (context, url, error) =>
const Icon(Icons.error),
),

View File

@@ -184,9 +184,12 @@ class _MihMyBusinessUserState extends State<MihMyBusinessUser> {
child: Column(
children: [
Center(
child: MihCircleAvatar(
child: Stack(
children: [
MihCircleAvatar(
imageFile: mzansiProfileProvider.userProfilePicture,
width: 150,
expandable: true,
editable: false,
fileNameController: fileNameController,
userSelectedfile: userPicFile,
@@ -198,6 +201,31 @@ class _MihMyBusinessUserState extends State<MihMyBusinessUser> {
"Dark"),
onChange: (_) {},
),
Positioned(
bottom: 5,
right: 5,
child: MihButton(
onPressed: () {
editBizUserProfileWindow(
mzansiProfileProvider, width);
},
buttonColor: MihColors.getGreenColor(
MzansiInnovationHub.of(context)!.theme.mode ==
"Dark"),
width: 35,
height: 35,
child: Icon(
Icons.edit,
color: MihColors.getPrimaryColor(
MzansiInnovationHub.of(context)!
.theme
.mode ==
"Dark"),
),
),
),
],
),
),
const SizedBox(height: 20),
buildEmployeeInfoCard(mzansiProfileProvider),
@@ -246,28 +274,6 @@ class _MihMyBusinessUserState extends State<MihMyBusinessUser> {
),
),
const SizedBox(height: 20),
Center(
child: MihButton(
onPressed: () {
editBizUserProfileWindow(
mzansiProfileProvider, width);
},
buttonColor: MihColors.getGreenColor(
MzansiInnovationHub.of(context)!.theme.mode ==
"Dark"),
width: 300,
child: Text(
"Edit Profile",
style: TextStyle(
color: MihColors.getPrimaryColor(
MzansiInnovationHub.of(context)!.theme.mode ==
"Dark"),
fontSize: 20,
fontWeight: FontWeight.bold,
),
),
),
),
],
),
),

View File

@@ -343,6 +343,7 @@ class _MihEditPersonalProfileWindowState
? MemoryImage(newSelectedProPic!.bytes!)
: mzansiProfileProvider.userProfilePicture,
width: 150,
expandable: false,
editable: true,
fileNameController: proPicController,
userSelectedfile: newSelectedProPic,

View File

@@ -160,6 +160,7 @@ class _MihPersonalProfileState extends State<MihPersonalProfile> {
MihCircleAvatar(
imageFile: mzansiProfileProvider.userProfilePicture,
width: 150,
expandable: true,
editable: false,
fileNameController: proPicController,
userSelectedfile: newSelectedProPic,

View File

@@ -74,6 +74,7 @@ class _MihPersonalProfileViewState extends State<MihPersonalProfileView> {
imageFile: CachedNetworkImageProvider(
asyncSnapshot.requireData),
width: profilePictureWidth,
expandable: true,
editable: false,
fileNameController: TextEditingController(),
userSelectedfile: file,

View File

@@ -1,3 +1,5 @@
import 'dart:io';
import 'package:flutter/foundation.dart';
import 'package:flutter_speed_dial/flutter_speed_dial.dart';
import 'package:go_router/go_router.dart';
@@ -545,7 +547,7 @@ class _BuildLoyaltyCardListState extends State<BuildLoyaltyCardList> {
),
),
SizedBox(height: 10),
MihBannerAd()
if (!kIsWeb && (Platform.isAndroid || Platform.isIOS)) MihBannerAd()
// MihBannerAd(),
],
),
@@ -572,7 +574,7 @@ class _BuildLoyaltyCardListState extends State<BuildLoyaltyCardList> {
}
Future<void> setScreenBrightness(double newBrightness) async {
if (!kIsWeb) {
if (!kIsWeb && !Platform.isLinux) {
bool canChange =
await ScreenBrightness.instance.canChangeSystemBrightness;

View File

@@ -314,6 +314,7 @@ class _PatientInfoState extends State<PatientInfo> {
imageFile:
patientManagerProvider.selectedPatientProfilePicture,
width: 160,
expandable: true,
editable: false,
fileNameController: null,
userSelectedfile: null,

View File

@@ -1,3 +1,5 @@
import 'dart:io';
import 'package:flutter/foundation.dart';
import 'package:google_mobile_ads/google_mobile_ads.dart';
import 'package:mzansi_innovation_hub/mih_config/mih_env.dart';
@@ -28,6 +30,7 @@ class MihBannerAdProvider extends ChangeNotifier {
}
void loadBannerAd() {
if (!kIsWeb && (Platform.isAndroid || Platform.isIOS)) {
if (bannerAd != null) {
bannerAd!.dispose();
bannerAd = null;
@@ -60,3 +63,4 @@ class MihBannerAdProvider extends ChangeNotifier {
bannerAd!.load();
}
}
}

View File

@@ -1,3 +1,6 @@
import 'dart:io';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:mzansi_innovation_hub/main.dart';
@@ -134,7 +137,9 @@ class MihAlertServices {
),
const SizedBox(height: 15),
Text(
"To get the most out of MIH, we need your location. Please go to the site settings of the app and enable location services. Once you do that, we can start showing you relevant information based on your location.",
!kIsWeb && Platform.isLinux
? "To get the most out of MIH, we need your location. Please go to your System Settings and enable location services. Once you do that, we can start showing you relevant information based on your location."
: "To get the most out of MIH, we need your location. Please go to the site settings of the app and enable location services. Once you do that, we can start showing you relevant information based on your location.",
style: TextStyle(
color: MihColors.getSecondaryColor(
MzansiInnovationHub.of(context)!.theme.mode ==

View File

@@ -1,7 +1,10 @@
import 'dart:convert';
import 'dart:io';
import 'package:file_picker/file_picker.dart';
import 'package:flutter/foundation.dart';
import 'package:go_router/go_router.dart';
import 'package:ken_logger/ken_logger.dart';
import 'package:mzansi_innovation_hub/mih_package_components/mih_loading_circle.dart';
import 'package:mzansi_innovation_hub/mih_config/mih_env.dart';
import 'package:flutter/material.dart';
@@ -51,6 +54,15 @@ class MihFileApi {
} finally {
// Navigator.of(context).pop(); // Always pop loading dialog
}
KenLogger.success("File URL: $fileUrl");
if (AppEnviroment.getEnv() == "Dev" && kIsWeb) {
fileUrl = fileUrl.replaceAll("10.0.2.2", "127.0.0.1");
} else if (AppEnviroment.getEnv() == "Dev" && Platform.isIOS) {
fileUrl = fileUrl.replaceAll("10.0.2.2", "127.0.0.1");
} else if (AppEnviroment.getEnv() == "Dev" && Platform.isLinux) {
fileUrl = fileUrl.replaceAll("10.0.2.2", "127.0.0.1");
}
KenLogger.success("File URL: $fileUrl");
return fileUrl;
}

View File

@@ -1,3 +1,6 @@
import 'dart:io';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:geolocator/geolocator.dart';
import 'package:mzansi_innovation_hub/mih_services/mih_alert_services.dart';
@@ -13,6 +16,12 @@ class MIHLocationAPI {
///if user has blocked permission (denied or denied forver), user will get error pop up.
///if user has granted permission (while in use), function will return Position object.
Future<Position?> getGPSPosition(BuildContext context) async {
bool serviceEnabled = await Geolocator.isLocationServiceEnabled();
if (!serviceEnabled && !kIsWeb && Platform.isLinux) {
// Direct the user to their System Settings
MihAlertServices().locationPermissionAlert(context);
return null;
}
print("Before checkPermission"); // Debug
LocationPermission permission = await Geolocator.checkPermission();
print("After checkPermission: $permission"); // Debug

View File

@@ -4,10 +4,10 @@ project(runner LANGUAGES CXX)
# The name of the executable created for the application. Change this to change
# the on-disk name of your application.
set(BINARY_NAME "patient_manager")
set(BINARY_NAME "mzansi_innovation_hub")
# The unique GTK application identifier for this application. See:
# https://wiki.gnome.org/HowDoI/ChooseApplicationID
set(APPLICATION_ID "com.example.patient_manager")
set(APPLICATION_ID "za.co.mzansiinnovationhub.mih")
# Explicitly opt in to modern CMake behaviors to avoid warnings with recent
# versions of CMake.

Binary file not shown.

After

Width:  |  Height:  |  Size: 169 KiB

View File

@@ -0,0 +1,11 @@
[Desktop Entry]
Version=1.2.6
Type=Application
Name=MIH App
Comment=First Super App of Mzansi
Exec=mzansi_innovation_hub
Icon=za.co.mzansiinnovationhub.mih
Terminal=false
Categories=Utility;
Keywords=Mzansi;Innovation;Hub;App;
StartupWMClass=mzansi_innovation_hub

View File

@@ -0,0 +1,36 @@
<?xml version="1.0" encoding="UTF-8"?>
<component type="desktop-application">
<id>za.co.mzansiinnovationhub.mih</id>
<metadata_license>CC0-1.0</metadata_license>
<project_license>GPL-3.0</project_license>
<name>MIH App</name>
<summary>First Super App of Mzansi</summary>
<developer_name>Mzansi Innovation Hub</developer_name>
<description>
<p>
The Mzansi Innovation Hub (MIH) App is a multipurpose platform designed to
empower users with integrated digital services.
</p>
</description>
<launchable type="desktop-id">za.co.mzansiinnovationhub.mih.desktop</launchable>
<screenshots>
<screenshot type="default">
<caption>The main dashboard of the MIH App</caption>
<image>https://git.mzansi-innovation-hub.co.za/yaso_meth/mih-project/raw/branch/main/mih_ui/mih_app_flatpak/main.png</image>
</screenshot>
</screenshots>
<url type="homepage">https://mzansi-innovation-hub.co.za</url>
<url type="bugtracker">https://git.mzansi-innovation-hub.co.za/yaso_meth/mih-project/issues</url>
<content_rating type="oars-1.1" />
<releases>
<release version="1.2.6" date="2026-02-25" />
</releases>
</component>

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

View File

@@ -0,0 +1,46 @@
app-id: za.co.mzansiinnovationhub.mih
runtime: org.gnome.Platform
runtime-version: '49' # Matches a more modern GNOME stack
sdk: org.gnome.Sdk
command: mzansi_innovation_hub
finish-args:
- --share=ipc
- --socket=fallback-x11
- --socket=wayland
- --device=dri
- --socket=pulseaudio
- --share=network
- --talk-name=org.freedesktop.Notifications
# Filesystem access for documents
- --filesystem=xdg-documents:ro
modules:
- name: mih-app
buildsystem: simple
build-commands:
# Create directory structure
- mkdir -p /app/bin /app/share/mih-app /app/share/applications /app/share/metainfo /app/share/icons/hicolor/256x256/apps
# Install everything to the share folder
- cp -r * /app/share/mih-app/
- find /app/share/mih-app -name "mzansi_innovation_hub" -exec chmod +x {} +
# Link the binary to /app/bin so Flatpak can find it
- ln -s /app/share/mih-app/mzansi_innovation_hub /app/bin/mzansi_innovation_hub
# Install Integration Files
- install -Dm644 za.co.mzansiinnovationhub.mih.desktop /app/share/applications/za.co.mzansiinnovationhub.mih.desktop
- install -Dm644 za.co.mzansiinnovationhub.mih.metainfo.xml /app/share/metainfo/za.co.mzansiinnovationhub.mih.metainfo.xml
- install -Dm644 za.co.mzansiinnovationhub.mih.png /app/share/icons/hicolor/256x256/apps/za.co.mzansiinnovationhub.mih.png
sources:
- type: archive
url: https://git.mzansi-innovation-hub.co.za/yaso_meth/mih-project/releases/download/V.1.2.6/mzansi_innovation_hub.tar.gz
sha256: cd610351f334fccce2d279f3112a5ac21bfa18b39d3c59e2cb0334fe2e8019b3
- type: file
path: za.co.mzansiinnovationhub.mih.desktop
- type: file
path: za.co.mzansiinnovationhub.mih.metainfo.xml
- type: file
path: za.co.mzansiinnovationhub.mih.png

View File

@@ -369,6 +369,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "3.1.0"
dbus:
dependency: transitive
description:
name: dbus
sha256: d0c98dcd4f5169878b6cf8f6e0a52403a9dff371a3e2f019697accbf6f44a270
url: "https://pub.dev"
source: hosted
version: "0.7.12"
device_info_plus:
dependency: transitive
description:
@@ -808,6 +816,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "4.0.0"
geoclue:
dependency: transitive
description:
name: geoclue
sha256: c2a998c77474fc57aa00c6baa2928e58f4b267649057a1c76738656e9dbd2a7f
url: "https://pub.dev"
source: hosted
version: "0.1.1"
geolocator:
dependency: "direct main"
description:
@@ -832,6 +848,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.3.13"
geolocator_linux:
dependency: "direct main"
description:
name: geolocator_linux
sha256: d64112a205931926f4363bb6bd48f14cb38e7326833041d170615586cd143797
url: "https://pub.dev"
source: hosted
version: "0.2.4"
geolocator_platform_interface:
dependency: transitive
description:
@@ -904,6 +928,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.3.2"
gsettings:
dependency: transitive
description:
name: gsettings
sha256: "1b0ce661f5436d2db1e51f3c4295a49849f03d304003a7ba177d01e3a858249c"
url: "https://pub.dev"
source: hosted
version: "0.2.8"
html:
dependency: transitive
description:

View File

@@ -1,7 +1,7 @@
name: mzansi_innovation_hub
description: ""
publish_to: 'none' # Remove this line if you wish to publish to pub.dev
version: 1.2.6+127
version: 1.2.6+130
# version: 1.1.1+97 #--- Updated version for upgrader package testing
environment:
@@ -29,6 +29,7 @@ dependencies:
flutter_native_splash: ^2.4.6
printing: ^5.13.3
geolocator: ^14.0.1
geolocator_linux: ^0.2.4
table_calendar: ^3.1.2
youtube_player_iframe: ^5.2.0
mobile_scanner: ^7.0.1