first commit for mih package toolkit
This commit is contained in:
parent
3a90e777a8
commit
76999db7c5
168 changed files with 10542 additions and 71 deletions
272
lib/src/mih_package_tile.dart
Normal file
272
lib/src/mih_package_tile.dart
Normal file
|
|
@ -0,0 +1,272 @@
|
|||
import 'package:app_settings/app_settings.dart';
|
||||
import 'package:local_auth/local_auth.dart';
|
||||
import 'package:mih_package_toolkit/src/mih_button.dart';
|
||||
import 'package:mih_package_toolkit/src/mih_colors.dart';
|
||||
import 'package:mih_package_toolkit/src/mih_package_window.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:mih_package_toolkit/src/mih_single_child_scroll.dart';
|
||||
|
||||
/// A touchable "App Tile" used for launching modules within the MIH ecosystem.
|
||||
///
|
||||
/// [MihPackageTile] provides a standardized way to display application icons
|
||||
/// and names. It includes sophisticated features like:
|
||||
/// * **Biometric Ready**: Integrated with `local_auth` for secure access.
|
||||
/// * **Video Hints**: Can display a help window with a YouTube video if [ytVideoID] is provided.
|
||||
/// * **Responsive Scaling**: Uses [FittedBox] to ensure the [packageIcon] and [packageName]
|
||||
/// fit perfectly within the specified [iconSize].
|
||||
///
|
||||
/// ### Example:
|
||||
/// ```dart
|
||||
/// MihPackageTile(
|
||||
/// packageName: "Settings",
|
||||
/// packageIcon: Icon(Icons.settings, size: 50),
|
||||
/// iconSize: 100.0,
|
||||
/// textColor: Colors.white,
|
||||
/// onTap: () => print("Opening Settings..."),
|
||||
/// ytVideoID: "dQw4w9WgXcQ", // Optional help video
|
||||
/// )
|
||||
/// ```
|
||||
class MihPackageTile extends StatefulWidget {
|
||||
/// The name of the package displayed below the icon.
|
||||
final String packageName;
|
||||
// final String? ytVideoID;
|
||||
final Widget packageIcon;
|
||||
|
||||
/// The callback triggered when the tile is tapped.
|
||||
final void Function() onTap;
|
||||
|
||||
/// The total height and width of the tile container.
|
||||
final double iconSize;
|
||||
|
||||
/// The color of the [packageName] text.
|
||||
final Color textColor;
|
||||
// final bool? authenticateUser;
|
||||
const MihPackageTile({
|
||||
super.key,
|
||||
required this.onTap,
|
||||
required this.packageName,
|
||||
// this.ytVideoID,
|
||||
required this.packageIcon,
|
||||
required this.iconSize,
|
||||
required this.textColor,
|
||||
// this.authenticateUser,
|
||||
});
|
||||
|
||||
@override
|
||||
State<MihPackageTile> createState() => _MihPackageTileState();
|
||||
}
|
||||
|
||||
class _MihPackageTileState extends State<MihPackageTile> {
|
||||
final LocalAuthentication _auth = LocalAuthentication();
|
||||
|
||||
// void displayHint() {
|
||||
// if (widget.ytVideoID != null) {
|
||||
// showDialog(
|
||||
// barrierDismissible: false,
|
||||
// context: context,
|
||||
// builder: (context) {
|
||||
// return MihPackageWindow(
|
||||
// fullscreen: false,
|
||||
// windowTitle: widget.packageName,
|
||||
// // windowTools: const [],
|
||||
// onWindowTapClose: () {
|
||||
// Navigator.pop(context);
|
||||
// },
|
||||
// windowBody: SizedBox(),
|
||||
// //MIHYTVideoPlayer(videoYTLink: widget.ytVideoID!),
|
||||
// );
|
||||
// },
|
||||
// );
|
||||
// }
|
||||
// }
|
||||
|
||||
Future<bool> isUserAuthenticated() async {
|
||||
final bool canAuthWithBio = await _auth.canCheckBiometrics;
|
||||
final bool canAuthenticate =
|
||||
canAuthWithBio || await _auth.isDeviceSupported();
|
||||
if (canAuthenticate) {
|
||||
try {
|
||||
final bool didBioAuth = await _auth.authenticate(
|
||||
localizedReason: "Authenticate to access ${widget.packageName}",
|
||||
options: const AuthenticationOptions(biometricOnly: false),
|
||||
);
|
||||
if (didBioAuth) {
|
||||
return true;
|
||||
} else {
|
||||
authErrorPopUp();
|
||||
}
|
||||
// print("Authenticated: $didBioAuth");
|
||||
} catch (error) {
|
||||
authErrorPopUp();
|
||||
}
|
||||
} else {
|
||||
authErrorPopUp();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void authErrorPopUp() {
|
||||
showDialog(
|
||||
context: context,
|
||||
barrierDismissible: false,
|
||||
builder: (context) {
|
||||
return MihPackageWindow(
|
||||
fullscreen: false,
|
||||
windowTitle: null,
|
||||
onWindowTapClose: null,
|
||||
backgroundColor: MihColors.red(),
|
||||
windowBody: MihSingleChildScroll(
|
||||
scrollbarOn: true,
|
||||
child: Column(
|
||||
children: [
|
||||
Icon(
|
||||
Icons.warning_amber_rounded,
|
||||
size: 150,
|
||||
color: MihColors.secondary(),
|
||||
),
|
||||
Text(
|
||||
"Biometric Authentication Required",
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
color: MihColors.secondary(),
|
||||
fontSize: 25,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 15),
|
||||
Center(
|
||||
child: Text(
|
||||
"Hi there! To jump into the ${widget.packageName} Package, you'll need to authenticate yourself with your devices biometrics, please set up biometric authentication (like fingerprint, face ID, pattern or pin) on your device first.\n\nIf you have already set up biometric authentication, press \"Authenticate now\" to try again or press \"Set Up Authentication\" to go to your device settings.",
|
||||
style: TextStyle(
|
||||
color: MihColors.secondary(),
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 15),
|
||||
Center(
|
||||
child: Wrap(
|
||||
spacing: 10.0,
|
||||
runSpacing: 10.0,
|
||||
alignment: WrapAlignment.center,
|
||||
children: [
|
||||
MihButton(
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
buttonColor: MihColors.secondary(),
|
||||
width: 300,
|
||||
child: Text(
|
||||
"Dismiss",
|
||||
style: TextStyle(
|
||||
color: MihColors.primary(),
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
),
|
||||
MihButton(
|
||||
onPressed: () {
|
||||
AppSettings.openAppSettings(
|
||||
type: AppSettingsType.security,
|
||||
);
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
buttonColor: MihColors.primary(),
|
||||
width: 300,
|
||||
child: Text(
|
||||
"Set Up Authentication",
|
||||
style: TextStyle(
|
||||
color: MihColors.secondary(),
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
),
|
||||
MihButton(
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop();
|
||||
authenticateUser();
|
||||
},
|
||||
buttonColor: MihColors.green(),
|
||||
width: 300,
|
||||
child: Text(
|
||||
"Authenticate Now",
|
||||
style: TextStyle(
|
||||
color: MihColors.primary(),
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> authenticateUser() async {
|
||||
// if (widget.authenticateUser != null &&
|
||||
// widget.authenticateUser! &&
|
||||
// !kIsWeb &&
|
||||
// !Platform.isLinux) {
|
||||
// if (await isUserAuthenticated()) {
|
||||
// widget.onTap();
|
||||
// }
|
||||
// } else {
|
||||
// widget.onTap();
|
||||
// }
|
||||
widget.onTap();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
alignment: Alignment.topCenter,
|
||||
// color: Colors.black,
|
||||
width: widget.iconSize,
|
||||
height: widget.iconSize,
|
||||
child: GestureDetector(
|
||||
onTap: () async {
|
||||
authenticateUser();
|
||||
},
|
||||
onLongPress: null, // Do this later
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Expanded(
|
||||
child: FittedBox(
|
||||
fit: BoxFit.contain,
|
||||
alignment: Alignment.center,
|
||||
child: widget.packageIcon,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
Padding(
|
||||
// Add a little padding for better visual spacing
|
||||
padding: const EdgeInsets.symmetric(horizontal: 4.0),
|
||||
child: FittedBox(
|
||||
child: Text(
|
||||
widget.packageName,
|
||||
textAlign: TextAlign.center, // This centers the text content
|
||||
maxLines: 1, // Allow up to 2 lines to prevent clipping
|
||||
style: TextStyle(
|
||||
color: widget.textColor,
|
||||
fontSize: 20.0,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue