Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 47 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# AGENTS.md

## Cursor Cloud specific instructions

### What this repo is
`flexus_framework` is a **Flutter library/package** (`pubspec.yaml` → `project_type: package`),
not a standalone application. It has no `android/`, `ios/`, or `web/` runner directory and no
app `main()` — it is consumed by other Flutter apps. The normal development workflow is therefore
`flutter pub get` → `flutter analyze` → `flutter test`.

### Toolchain (already installed in the VM snapshot)
- **Flutter 3.7.12 / Dart 2.19.6** at `~/flutter` (on `PATH` via `~/.bashrc`). This is the newest
stable Flutter whose Dart is `< 3.0.0`, which is required by `pubspec.yaml`
(`sdk: ">=2.12.0 <3.0.0"`) and `pubspec.lock` (`dart: ">=2.14.0 <3.0.0"`, `flutter: ">=2.10.0-0"`).
Do not upgrade to a Dart 3 Flutter — `pub get` will reject it.
- **Android SDK** at `~/android-sdk` (platform `android-33`, `build-tools;33.0.2`, `platform-tools`).
`flutter config --android-sdk ~/android-sdk` is already set.
- **JDK 17** at `/usr/lib/jvm/java-17-openjdk-amd64` (exported as `JAVA_HOME` in `~/.bashrc`).
JDK 17 is required: the Gradle wrapper that Flutter 3.7 generates does **not** support the VM's
default JDK 21, so Android Gradle tasks fail under JDK 21.
- `~/.bashrc` exports `JAVA_HOME`, `ANDROID_SDK_ROOT`, `ANDROID_HOME` and adds Flutter + Android
tools to `PATH`. Non-login/non-interactive shells may not source it, so when scripting use the
absolute binary path `~/flutter/bin/flutter` if `flutter` is not found.

### Lint / test (these work)
- Lint: `flutter analyze` (clean — no issues).
- Test: `flutter test` (the only test, `test/flexus_framework_test.dart`, has an empty `main()`, so
it reports "No tests ran" but the harness compiles and runs).

### Building this package into an app
`flexus_framework` is a library, so it is built/run through a host app (e.g. a small example app
with a path dependency on this repo). The dependencies were upgraded to versions that compile under
Flutter 3.7.12 / Dart 2.19 (`network_to_file_image 4.x`, `firebase_* 4.x/10.x/11.x`,
`image_cropper 4.x`, `flutter_form_builder`/`form_builder_validators 8.x`, `carousel_slider 5.x`,
etc.). `sizer` is intentionally kept at `2.0.15`: 3.x changes `DeviceType` to OS-based values
(no `mobile`/`tablet`) and re-exports `ScreenType`, which clashes with `get`.

Known build caveats for a consuming/host app (not the package itself):
- The host app's `android/app/build.gradle` must set `minSdkVersion 21` (cloud_firestore requires
`>= 19`); the Flutter default (`flutter.minSdkVersion`) is too low.
- After changing `package_info_plus` major versions, run `flutter clean` in the host app before a
web build, otherwise a stale `web_plugin_registrant.dart` references the removed
`package_info_plus_web` package.

### iOS
iOS builds **cannot run on Linux**: `flutter build ios` is not a registered subcommand here
(only `aar/apk/appbundle/bundle/web` are). iOS requires macOS + Xcode.
5 changes: 3 additions & 2 deletions lib/screens/front/front_controller.dart
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,11 @@ import '../../imports.dart';
import '../../services/auth_service.dart';

class FxFrontController extends GetxController {
CarouselController sliderController = CarouselController();
CarouselSliderController sliderController = CarouselSliderController();
var isLoading = false.obs;
var currentSlider = 0.obs;
final CarouselController loginSliderController = CarouselController();
final CarouselSliderController loginSliderController =
CarouselSliderController();

Future<void> signInWithGoogle() async {
isLoading.value = true;
Expand Down
5 changes: 3 additions & 2 deletions lib/screens/front/front_screen.dart
Original file line number Diff line number Diff line change
Expand Up @@ -132,8 +132,9 @@ class FxFrontScreen extends ScreenMaster<FxFrontController> {
child: ElevatedButton(
key: const ValueKey("logInButton"),
style: ElevatedButton.styleFrom(
primary: Get.theme.colorScheme.secondary,
onPrimary: Get.theme.colorScheme.onSecondary),
backgroundColor: Get.theme.colorScheme.secondary,
foregroundColor:
Get.theme.colorScheme.onSecondary),
child: Text(Trns.signIn.val),
onPressed: () {
Get.to(
Expand Down
3 changes: 2 additions & 1 deletion lib/screens/log_in/log_in_controller.dart
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ import '../../services/auth_service.dart';

class FxLogInController extends GetxController {
var isLoading = false.obs;
final CarouselController loginSliderController = CarouselController();
final CarouselSliderController loginSliderController =
CarouselSliderController();

Future<void> signInWithEmailAndPassword(String email, String password) async {
try {
Expand Down
1 change: 0 additions & 1 deletion lib/screens/log_in/log_in_screen.dart
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ class FxLoginScreen extends ScreenMaster<FxLogInController> {
return ScaffoldMaster(Trns.signIn.val,
body: Obx(
() => LoadingOverlay(
opacity: 0.0,
isLoading: controller.isLoading.value,
child: SingleChildScrollView(
child: Column(children: [
Expand Down
6 changes: 3 additions & 3 deletions lib/screens/log_in/sliders/forgot_password_slider.dart
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,9 @@ class FxForgotPasswordSlider extends GetView<FxLogInController> {
icon: Icons.email_outlined,
obscureText: false,
validator: FormBuilderValidators.compose([
FormBuilderValidators.required(context),
FormBuilderValidators.email(context),
FormBuilderValidators.maxLength(context, 50),
FormBuilderValidators.required(),
FormBuilderValidators.email(),
FormBuilderValidators.maxLength(50),
])),
const SizedBox(height: 32),
ConstrainedBox(
Expand Down
10 changes: 5 additions & 5 deletions lib/screens/log_in/sliders/login_slider.dart
Original file line number Diff line number Diff line change
Expand Up @@ -33,18 +33,18 @@ class FxLoginSlider extends GetView<FxLogInController> {
icon: Icons.email_outlined,
obscureText: false,
validator: FormBuilderValidators.compose([
FormBuilderValidators.required(context),
FormBuilderValidators.email(context),
FormBuilderValidators.maxLength(context, 50),
FormBuilderValidators.required(),
FormBuilderValidators.email(),
FormBuilderValidators.maxLength(50),
])),
TextInput(
name: 'password',
label: Trns.password.val,
icon: Icons.vpn_key,
obscureText: true,
validator: FormBuilderValidators.compose([
FormBuilderValidators.required(context),
FormBuilderValidators.maxLength(context, 50),
FormBuilderValidators.required(),
FormBuilderValidators.maxLength(50),
])),
const SizedBox(height: 16),
ConstrainedBox(
Expand Down
22 changes: 11 additions & 11 deletions lib/screens/log_in/sliders/sign_up_slider.dart
Original file line number Diff line number Diff line change
Expand Up @@ -33,38 +33,38 @@ class FxSignUpSlider extends GetView<FxSignUpController> {
icon: Icons.person_outline,
obscureText: false,
validator: FormBuilderValidators.compose([
FormBuilderValidators.required(Get.context!),
FormBuilderValidators.maxLength(Get.context!, 50),
FormBuilderValidators.required(),
FormBuilderValidators.maxLength(50),
])),
TextInput(
name: 'email',
label: Trns.email.val,
icon: Icons.email_outlined,
obscureText: false,
validator: FormBuilderValidators.compose([
FormBuilderValidators.required(Get.context!),
FormBuilderValidators.email(Get.context!),
FormBuilderValidators.maxLength(Get.context!, 50),
FormBuilderValidators.required(),
FormBuilderValidators.email(),
FormBuilderValidators.maxLength(50),
])),
TextInput(
name: 'password',
label: Trns.password.val,
icon: Icons.vpn_key,
obscureText: true,
validator: FormBuilderValidators.compose([
FormBuilderValidators.required(Get.context!),
FormBuilderValidators.maxLength(Get.context!, 50),
FormBuilderValidators.minLength(Get.context!, 8),
FormBuilderValidators.required(),
FormBuilderValidators.maxLength(50),
FormBuilderValidators.minLength(8),
])),
TextInput(
name: 'confirm_password',
label: Trns.confirmPassword.val,
icon: Icons.vpn_key,
obscureText: true,
validator: FormBuilderValidators.compose([
FormBuilderValidators.required(Get.context!),
FormBuilderValidators.maxLength(Get.context!, 50),
FormBuilderValidators.minLength(Get.context!, 8),
FormBuilderValidators.required(),
FormBuilderValidators.maxLength(50),
FormBuilderValidators.minLength(8),
(val) {
if (_formKey.currentState!.fields['password']?.value !=
val) {
Expand Down
2 changes: 1 addition & 1 deletion lib/screens/log_in/sliders/verify_email_slider.dart
Original file line number Diff line number Diff line change
Expand Up @@ -47,14 +47,14 @@ class FxVerifyEmailSlider extends GetView<FxLogInController> {
constraints:
BoxConstraints.tightFor(width: Get.width, height: 48),
child: ElevatedButton(
child: Text(Trns.next.val),
onPressed: AuthService.to.isEmailVerified.value
? () {
AuthService.to.authUser.value.isEmailVerified = true;
AuthService.to.afterLogin().then((value) =>
Get.off(() => Util.to.getHomeScreen()));
}
: null,
child: Text(Trns.next.val),
),
),
]),
Expand Down
2 changes: 1 addition & 1 deletion lib/screens/log_in/widgets/login_slider_master.dart
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ class FxLoginSliderMaster extends GetView<FxFrontController> {
heightFactor: 2,
child: Text(
title!,
style: Theme.of(context).textTheme.headline6,
style: Theme.of(context).textTheme.titleLarge,
),
),
],
Expand Down
63 changes: 33 additions & 30 deletions lib/screens/profile/profile_screen.dart
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@ class FxProfileScreen extends ScreenMaster<FxProfileController> {
return ScaffoldMaster(Trns.updateProfile.val,
body: Obx(
() => LoadingOverlay(
opacity: 0.0,
isLoading: profileController.isLoading.value,
child: Builder(builder: (context) {
return SingleChildScrollView(
Expand All @@ -59,16 +58,16 @@ class FxProfileScreen extends ScreenMaster<FxProfileController> {
padding: const EdgeInsets.only(left: 100),
child: ElevatedButton(
onPressed: _readPhoto,
style: ElevatedButton.styleFrom(
shape: const CircleBorder(),
backgroundColor:
Theme.of(context).colorScheme.primary,
),
child: const Icon(
Icons.add_a_photo,
color: Colors.white,
size: 16,
),
style: ElevatedButton.styleFrom(
shape: const CircleBorder(),
primary:
Theme.of(context).colorScheme.primary,
),
),
)
],
Expand All @@ -89,8 +88,8 @@ class FxProfileScreen extends ScreenMaster<FxProfileController> {
icon: Icons.person_outline,
obscureText: false,
validator: FormBuilderValidators.compose([
FormBuilderValidators.required(context),
FormBuilderValidators.maxLength(context, 50),
FormBuilderValidators.required(),
FormBuilderValidators.maxLength(50),
])),
_getGenderTextField(context),
TextDateTimePicker(
Expand All @@ -104,7 +103,7 @@ class FxProfileScreen extends ScreenMaster<FxProfileController> {
? false
: true,
validator: FormBuilderValidators.compose(
[FormBuilderValidators.required(context)]),
[FormBuilderValidators.required()]),
),

/*
Expand All @@ -119,7 +118,7 @@ class FxProfileScreen extends ScreenMaster<FxProfileController> {
enabled: AuthService.to.authUser.value.authType ==
AuthType.email,
validator: FormBuilderValidators.compose([
FormBuilderValidators.maxLength(context, 50),
FormBuilderValidators.maxLength(50),
(val) {
if (val != null && val.length < 8) {
return Trns.warningMinimumPasswordLength.val;
Expand All @@ -135,7 +134,7 @@ class FxProfileScreen extends ScreenMaster<FxProfileController> {
AuthType.email,
obscureText: true,
validator: FormBuilderValidators.compose([
FormBuilderValidators.maxLength(context, 50),
FormBuilderValidators.maxLength(50),
(val) {
if (_formKey.currentState!.fields['password']
?.value !=
Expand Down Expand Up @@ -172,7 +171,7 @@ class FxProfileScreen extends ScreenMaster<FxProfileController> {
var items = ["male", "female"];
var enabled = gender != null ? false : true;
String? Function(dynamic) validator = FormBuilderValidators.compose(
[FormBuilderValidators.required(context)]);
[FormBuilderValidators.required()]);
if (gender != null) {
return TextDropdown(
name: name,
Expand Down Expand Up @@ -215,15 +214,16 @@ class FxProfileScreen extends ScreenMaster<FxProfileController> {
const BoxConstraints(minWidth: 64, minHeight: 64),
child: ElevatedButton(
onPressed: () => _getImage(ImageSource.camera),
style: ElevatedButton.styleFrom(
shape: const CircleBorder(),
backgroundColor:
Theme.of(Get.context!).colorScheme.primary,
),
child: const Icon(
Icons.camera_alt,
color: Colors.white,
size: 40,
),
style: ElevatedButton.styleFrom(
shape: const CircleBorder(),
primary: Theme.of(Get.context!).colorScheme.primary,
),
),
),
const SizedBox(height: 8),
Expand All @@ -238,15 +238,16 @@ class FxProfileScreen extends ScreenMaster<FxProfileController> {
const BoxConstraints(minWidth: 64, minHeight: 64),
child: ElevatedButton(
onPressed: () => _getImage(ImageSource.gallery),
style: ElevatedButton.styleFrom(
shape: const CircleBorder(),
backgroundColor:
Theme.of(Get.context!).colorScheme.primary,
),
child: const Icon(
Icons.photo,
color: Colors.white,
size: 40,
),
style: ElevatedButton.styleFrom(
shape: const CircleBorder(),
primary: Theme.of(Get.context!).colorScheme.primary,
),
),
),
const SizedBox(height: 8),
Expand All @@ -262,22 +263,24 @@ class FxProfileScreen extends ScreenMaster<FxProfileController> {
_getImage(ImageSource imageSource) async {
Get.back();
final pickedFile = await (imagePicker.pickImage(source: imageSource));
File? croppedFile = await (ImageCropper.cropImage(
CroppedFile? croppedFile = await ImageCropper().cropImage(
sourcePath: pickedFile!.path,
maxWidth: 512,
maxHeight: 512,
aspectRatioPresets: [
CropAspectRatioPreset.square,
],
androidUiSettings: const AndroidUiSettings(
toolbarTitle: "Crop Image",
toolbarColor: Colors.black,
toolbarWidgetColor: Colors.white,
initAspectRatio: CropAspectRatioPreset.square,
lockAspectRatio: true),
iosUiSettings: const IOSUiSettings(
minimumAspectRatio: 1.0,
)));
uiSettings: [
AndroidUiSettings(
toolbarTitle: "Crop Image",
toolbarColor: Colors.black,
toolbarWidgetColor: Colors.white,
initAspectRatio: CropAspectRatioPreset.square,
lockAspectRatio: true),
IOSUiSettings(
minimumAspectRatio: 1.0,
),
]);
controller.imageFile.value = File(croppedFile!.path);
}
}
1 change: 0 additions & 1 deletion lib/screens/sign_up/sign_up_screen.dart
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ class FxSignUpScreen extends ScreenMaster<FxSignUpController> {
return ScaffoldMaster(Trns.signUp.val,
body: Obx(
() => LoadingOverlay(
opacity: 0.0,
isLoading: controller.isLoading.value,
child: SingleChildScrollView(
child: Column(children: [
Expand Down
1 change: 0 additions & 1 deletion lib/screens/splash/splash_controller.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import 'package:firebase_auth/firebase_auth.dart';

import '../../../../consts/login_sliders.dart';
import '../../../../flexus_framework.dart';
import '../../../../screens/front/front_screen.dart';
import '../../../../screens/log_in/log_in_screen.dart';
import '../../imports.dart';
import '../../services/auth_service.dart';
Expand Down
2 changes: 1 addition & 1 deletion lib/util.dart
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ class Util extends GetxController {
lineLength: 50,
colors: true,
printEmojis: true,
printTime: false,
dateTimeFormat: DateTimeFormat.none,
));

Logger logger() {
Expand Down
Loading