Skip to content
Merged
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
27 changes: 27 additions & 0 deletions .github/actions/setup-flutter/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Single source of truth for the Flutter SDK version used by CI.
# Bump `flutter-version`'s default below and every workflow follows.
#
# Workflows that need a different SDK (build.yml builds the starter on the
# oldest supported SDK as well) pass `flutter-version` explicitly.

name: Setup Flutter
description: Installs the Flutter SDK version this repository pins for CI.

inputs:
flutter-version:
description: Flutter SDK version to install. Defaults to the repo-wide pin.
required: false
default: "3.47.2"
cache:
description: Cache the installed SDK between runs.
required: false
default: "true"

runs:
using: composite
steps:
- name: Setup Flutter ${{ inputs.flutter-version }}
uses: subosito/flutter-action@v2
with:
flutter-version: ${{ inputs.flutter-version }}
cache: ${{ inputs.cache }}
52 changes: 49 additions & 3 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ jobs:

strategy:
matrix:
flutter-version: ["3.38.7"]
flutter-version: ["3.38.7", "3.47.2"]
platform: ["web", "android"]

steps:
Expand All @@ -23,10 +23,9 @@ jobs:
java-version: '17'

- name: Setup Flutter SDK
uses: subosito/flutter-action@v2
uses: ./.github/actions/setup-flutter
with:
flutter-version: ${{ matrix.flutter-version }}
cache: true

- name: Install Melos
run: dart pub global activate melos
Expand Down Expand Up @@ -74,3 +73,50 @@ jobs:
starter/build/web/**
starter/build/app/outputs/flutter-apk/*-release.apk
retention-days: 7

# Guards the minimum-SDK web configuration: downstream apps that still need the
# HTML web renderer (removed in Flutter 3.29) are pinned to 3.27.2 and consume
# only the core `ensemble` package. A full `melos bootstrap` cannot run on 3.27
# (auth, chat and ensemble_bluetooth pin collection ^1.19.1 / meta ^1.16.0,
# unsatisfiable there), so the bootstrap is scoped to the starter and its
# dependencies.
buildStarterWebMinSdk:
name: Build Starter Web (Flutter 3.27.2, core only)
runs-on: ubuntu-latest

steps:
- name: Checkout Code
uses: actions/checkout@v4

- name: Setup Flutter SDK
uses: ./.github/actions/setup-flutter
with:
flutter-version: "3.27.2"

- name: Assert the starter enables no optional modules
run: |
count=$(awk '/^dependencies:/,/^dev_dependencies:/' starter/pubspec.yaml \
| grep -cE '^ ensemble[a-z_]*:')
if [ "$count" -ne 1 ]; then
echo "::error::This job builds the minimum-SDK web configuration, which uses only the core ensemble package. Found $count enabled ensemble* dependencies in starter/pubspec.yaml, expected 1."
exit 1
fi

# Pinned: Flutter 3.27.2 ships Dart 3.6.1, and melos >=6.3.3 requires Dart >=3.8.
- name: Install Melos
run: dart pub global activate melos 6.3.2

- name: Bootstrap the core packages only
run: melos bootstrap --scope="ensemble_starter" --include-dependencies

- name: Build web with the HTML renderer
run: |
cd starter
flutter build web --release --web-renderer html --no-tree-shake-icons

- name: Upload Build Artifacts
uses: actions/upload-artifact@v4
with:
name: starter-build-web-flutter-3.27.2-html
path: starter/build/web/**
retention-days: 7
5 changes: 1 addition & 4 deletions .github/workflows/pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,7 @@ jobs:
uses: actions/checkout@v3

- name: Setup Flutter SDK
uses: subosito/flutter-action@v2
with:
flutter-version: '3.32.5'
cache: true
uses: ./.github/actions/setup-flutter

- name: Install Melos
run: dart pub global activate melos
Expand Down
10 changes: 2 additions & 8 deletions .github/workflows/publish-ensemble-pubdev.yml
Original file line number Diff line number Diff line change
Expand Up @@ -58,10 +58,7 @@ jobs:

- name: Setup Flutter SDK
if: steps.validate-release-tag.outputs.should_publish == 'true'
uses: subosito/flutter-action@v2
with:
flutter-version: "3.32.5"
cache: true
uses: ./.github/actions/setup-flutter

- name: Setup pub.dev credentials
if: steps.validate-release-tag.outputs.should_publish == 'true'
Expand Down Expand Up @@ -254,10 +251,7 @@ jobs:
ref: ${{ matrix.package.name }}-v${{ needs.publish.outputs.version }}

- name: Setup Flutter SDK
uses: subosito/flutter-action@v2
with:
flutter-version: "3.32.5"
cache: true
uses: ./.github/actions/setup-flutter

- name: Setup pub.dev credentials
uses: dart-lang/setup-dart@v1
Expand Down
5 changes: 1 addition & 4 deletions .github/workflows/publish-standalone-pubdev.yml
Original file line number Diff line number Diff line change
Expand Up @@ -82,10 +82,7 @@ jobs:
ref: ${{ github.ref_name }}

- name: Setup Flutter SDK
uses: subosito/flutter-action@v2
with:
flutter-version: "3.32.5"
cache: true
uses: ./.github/actions/setup-flutter

- name: Setup pub.dev credentials
uses: dart-lang/setup-dart@v1
Expand Down
5 changes: 1 addition & 4 deletions .github/workflows/release-beta-version.yml
Original file line number Diff line number Diff line change
Expand Up @@ -98,10 +98,7 @@ jobs:
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"

- name: Setup Flutter SDK
uses: subosito/flutter-action@v2
with:
flutter-version: "3.32.5"
cache: true
uses: ./.github/actions/setup-flutter

- name: Install Melos
run: dart pub global activate melos
Expand Down
5 changes: 1 addition & 4 deletions .github/workflows/release-melos-version.yml
Original file line number Diff line number Diff line change
Expand Up @@ -50,10 +50,7 @@ jobs:
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"

- name: Setup Flutter SDK
uses: subosito/flutter-action@v2
with:
flutter-version: "3.32.5"
cache: true
uses: ./.github/actions/setup-flutter

- name: Install Melos
run: dart pub global activate melos
Expand Down
5 changes: 1 addition & 4 deletions .github/workflows/release-standalone-package.yml
Original file line number Diff line number Diff line change
Expand Up @@ -62,10 +62,7 @@ jobs:
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"

- name: Setup Flutter SDK
uses: subosito/flutter-action@v2
with:
flutter-version: "3.32.5"
cache: true
uses: ./.github/actions/setup-flutter

- name: Install Melos
run: dart pub global activate melos
Expand Down
4 changes: 1 addition & 3 deletions .github/workflows/starter-commands.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,7 @@ jobs:
uses: actions/checkout@v2

- name: Set up Flutter SDK
uses: subosito/flutter-action@v2
with:
channel: 'stable'
uses: ./.github/actions/setup-flutter

- name: Set up Node.js
uses: actions/setup-node@v2
Expand Down
29 changes: 27 additions & 2 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,16 @@
Ensemble is a Flutter/Dart monorepo (managed by Melos) that provides a runtime for building native apps from declarative YAML definitions. The starter app (`/starter`) is the primary runnable application.

### Prerequisites
- **Flutter SDK 3.38.7** must be installed at `/opt/flutter` with `/opt/flutter/bin` on `PATH`.
- **Flutter SDK 3.47.2** must be installed at `/opt/flutter` with `/opt/flutter/bin` on `PATH`.
Every CI workflow pins it; `build.yml` additionally builds the starter on 3.38.7.
`modules/ensemble` declares `flutter: ">=3.27.0"` and was verified manually on 3.27.4, 3.35.4 and
3.47.2 — do not introduce APIs newer than 3.27 there without raising that floor (see the
`TODO(flutter-upgrade)` markers).
- CI does **not** test 3.27: `modules/auth`, `modules/chat` and `modules/ensemble_bluetooth` declare
`collection: ^1.19.1` / `meta: ^1.16.0`, which Flutter 3.27 cannot satisfy (it pins `collection 1.19.0`
and `meta 1.15.0` exactly). Pre-existing, unrelated to the core runtime — those modules are opt-in and
`modules/ensemble` does not depend on them.
- **JDK 17** is required for Android builds (Flutter 3.38 raised the minimum).
- **Melos** must be globally activated (`dart pub global activate melos`).
- `PATH` must include both `/opt/flutter/bin` and `$HOME/.pub-cache/bin`.

Expand All @@ -26,7 +35,23 @@ Ensemble is a Flutter/Dart monorepo (managed by Melos) that provides a runtime f
- The starter's `pubspec.yaml` points the `ensemble` dependency to a git URL, but `melos bootstrap` overrides this with a local path reference. Always run `melos bootstrap` from the repo root before building.
- The default `ensemble-config.yaml` uses `from: ensemble` which fetches app definitions from Ensemble's cloud (Firestore). This works without any local Firebase setup since it reads from a public Kitchen Sink demo app (`appId: e24402cb-75e2-404c-866c-29e6c3dd7992`).
- To use local YAML definitions instead, change `from: ensemble` to `from: local` in `starter/ensemble/ensemble-config.yaml`.
- `flutter analyze` will report ~520 pre-existing warnings in `modules/ensemble` (mostly unused imports and `must_be_immutable`). These are not regressions.
- `flutter analyze` reports ~237 pre-existing issues in `modules/ensemble` (0 errors; mostly `must_be_immutable`,
unused imports/locals, and ~22 remaining deprecations). These are not regressions.
- iOS/Android **release** builds need `--no-tree-shake-icons`: Ensemble resolves icons dynamically from YAML,
so it builds non-const `IconData` (see `modules/ensemble/lib/framework/widget/icon.dart`).
- The starter is **deliberately incomplete in git**: `.gitignore` excludes both the Xcode project
(`project.pbxproj`) and the Android `MainActivity` (`starter/android/app/src/main/kotlin`). Before
building either platform, run the generate step from the starter README:
`cd starter && flutter create --org com.ensembleui --project-name starter --platform=ios,android,web .`
The project name must yield `com.ensembleui.starter` to match `appId` in `ensemble/ensemble.properties`,
otherwise the app installs but crashes with `ClassNotFoundException ... MainActivity`.
- Before an **iOS** build on Flutter >= 3.44, disable SwiftPM: `flutter config --no-enable-swift-package-manager`.
10 plugins have no SwiftPM support, and running it alongside CocoaPods makes Xcode fail with
"Multiple commands produce ...framework" for the Firebase/gRPC frameworks. Not set in `pubspec.yaml`
on purpose: the `flutter: config:` key only parses on Flutter >= 3.44 and would break the 3.27 target.
CI is unaffected (it builds web + android only).
- Web builds produce Wasm compatibility warnings for packages using `dart:html` — these are informational and do not block the JS build.
- The Chrome device (`-d chrome`) opens a browser window; use `-d web-server` for headless/CI environments.
- After changing dependencies in any module's `pubspec.yaml`, re-run `melos bootstrap` from the repo root.
- After switching Flutter SDKs (`fvm use`), run `flutter clean` before testing — a stale per-SDK
`shaders/ink_sparkle.frag` makes unrelated widget tests fail with "Runtime stages buffer failed verification".
6 changes: 5 additions & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,11 @@ By participating in this project, you agree to maintain a respectful and inclusi

### Prerequisites

- [Flutter](https://docs.flutter.dev/get-started/install) (>= 3.24.0)
- [Flutter](https://docs.flutter.dev/get-started/install) **3.47.2** (the version all CI workflows pin;
`build.yml` also builds the starter on 3.38.7).
`modules/ensemble` supports `flutter: ">=3.27.0"`, verified on 3.27.4, 3.35.4 and 3.47.2. Keep it that
way: when a deprecation's replacement needs a newer SDK, keep the deprecated call with an
`// ignore: deprecated_member_use` and a `TODO(flutter-upgrade)` naming the version that unlocks it.
- [Melos](https://melos.invertase.dev/) (>= 3.4.0) — install with `dart pub global activate melos`

### Setting Up the Development Environment
Expand Down
4 changes: 3 additions & 1 deletion modules/ensemble/lib/action/bottom_sheet_actions.dart
Original file line number Diff line number Diff line change
Expand Up @@ -239,8 +239,10 @@ class ShowBottomSheetAction extends EnsembleAction with HasStyles, Invokable {
{required Widget child, required bool isScrollable}) {
Widget rootWidget = Container(
decoration: BoxDecoration(
// https://docs.flutter.dev/release/breaking-changes/deprecate-themedata-dialogbackgroundcolor
color: getBackgroundColor(scopeManager) ??
Theme.of(context).dialogBackgroundColor,
Theme.of(context).dialogTheme.backgroundColor ??
Theme.of(context).colorScheme.surface,
borderRadius: borderRadius(scopeManager)?.getValue() ??
const BorderRadius.only(
topLeft: defaultTopBorderRadius,
Expand Down
33 changes: 20 additions & 13 deletions modules/ensemble/lib/action/misc_action.dart
Original file line number Diff line number Diff line change
Expand Up @@ -155,11 +155,14 @@ class ShareAction extends EnsembleAction {
// Share files if any were created successfully
if (xFiles.isNotEmpty) {
try {
final result = await Share.shareXFiles(
xFiles,
text: evaluatedText,
subject: evaluatedTitle ?? '',
sharePositionOrigin: sharePositionOrigin,
// https://pub.dev/packages/share_plus/changelog
final result = await SharePlus.instance.share(
ShareParams(
files: xFiles,
text: evaluatedText,
subject: evaluatedTitle ?? '',
sharePositionOrigin: sharePositionOrigin,
),
);

// Handle share result
Expand All @@ -173,10 +176,12 @@ class ShareAction extends EnsembleAction {
debugPrint('Error sharing files: $e');
if (kIsWeb) {
// On web, fall back to sharing just the text
await Share.share(
evaluatedText,
subject: evaluatedTitle ?? '',
sharePositionOrigin: sharePositionOrigin,
await SharePlus.instance.share(
ShareParams(
text: evaluatedText,
subject: evaluatedTitle ?? '',
sharePositionOrigin: sharePositionOrigin,
),
);
return;
}
Expand All @@ -188,10 +193,12 @@ class ShareAction extends EnsembleAction {

// Fall back to sharing just text if no files or file sharing failed
if (evaluatedText.isNotEmpty) {
final result = await Share.share(
evaluatedText,
subject: evaluatedTitle ?? '',
sharePositionOrigin: sharePositionOrigin,
final result = await SharePlus.instance.share(
ShareParams(
text: evaluatedText,
subject: evaluatedTitle ?? '',
sharePositionOrigin: sharePositionOrigin,
),
);

if (result.status == ShareResultStatus.success) {
Expand Down
3 changes: 3 additions & 0 deletions modules/ensemble/lib/action/saveFile/download_web.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
// TODO(web-wasm): replace dart:html / package:js with package:web +
// dart:js_interop. Blocks the WebAssembly target only; JS build is fine.
// https://dart.dev/interop/js-interop/package-web
import 'dart:html' as html;

import 'package:flutter/foundation.dart';
Expand Down
7 changes: 3 additions & 4 deletions modules/ensemble/lib/ensemble_theme.dart
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,6 @@ class EnsembleTheme {
onSecondary: Colors.white,
error: Color(0xFFB00020),
onError: Colors.white,
background: Colors.white,
onBackground: Color(0xFF404040),
surface: Colors.white,
onSurface: Color(0xFF404040)));
}
Expand Down Expand Up @@ -144,7 +142,8 @@ class EnsembleTheme {
final ThemeData base = ThemeData.light();
return base.copyWith(
primaryColor: primaryColor,
indicatorColor: Colors.white,
// https://docs.flutter.dev/release/breaking-changes/deprecate-themedata-indicatorcolor
tabBarTheme: const TabBarThemeData(indicatorColor: Colors.white),
splashColor: Colors.white24,
splashFactory: InkRipple.splashFactory,
//accentColor: secondaryColor,
Expand All @@ -157,7 +156,7 @@ class EnsembleTheme {
textTheme: _buildTextThemeOld(base.textTheme),
primaryTextTheme: _buildTextThemeOld(base.primaryTextTheme),
//accentTextTheme: _buildTextThemeOld(base.accentTextTheme),
platform: TargetPlatform.iOS, colorScheme: colorScheme.copyWith(background: const Color(0xFFFFFFFF)),
platform: TargetPlatform.iOS, colorScheme: colorScheme.copyWith(surface: const Color(0xFFFFFFFF)),
);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,15 +97,16 @@ class FirebaseFunctionsAPIProvider extends APIProvider {
if (_app?.options.apiKey != null) {
FirebaseAppCheck appCheck = FirebaseAppCheck.instanceFor(app: _app!);
// Now initialize App Check
// https://pub.dev/packages/firebase_app_check/changelog
if (kDebugMode) {
await appCheck.activate(
androidProvider: AndroidProvider.debug,
appleProvider: AppleProvider.debug,
providerAndroid: const AndroidDebugProvider(),
providerApple: const AppleDebugProvider(),
);
} else {
await appCheck.activate(
androidProvider: AndroidProvider.playIntegrity,
appleProvider: AppleProvider.appAttest,
providerAndroid: const AndroidPlayIntegrityProvider(),
providerApple: const AppleAppAttestProvider(),
);
}

Expand Down
3 changes: 3 additions & 0 deletions modules/ensemble/lib/framework/config/url_parser_web.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
// TODO(web-wasm): replace dart:html / package:js with package:web +
// dart:js_interop. Blocks the WebAssembly target only; JS build is fine.
// https://dart.dev/interop/js-interop/package-web
import 'dart:html' as html;

bool getTestMode() {
Expand Down
4 changes: 2 additions & 2 deletions modules/ensemble/lib/framework/device.dart
Original file line number Diff line number Diff line change
Expand Up @@ -163,9 +163,10 @@ class DeviceWebInfo with Invokable {
Map<String, Function> getters() {
WebBrowserInfo? browserInfo = DeviceInfoCapability.browserInfo;
return {
// https://docs.flutter.dev/release/breaking-changes/remove-describeEnum
'browserName': () => browserInfo?.browserName == null
? null
: describeEnum(browserInfo!.browserName),
: browserInfo!.browserName.name,
'appCodeName': () => browserInfo?.appCodeName,
'appName': () => browserInfo?.appName,
'appVersion': () => browserInfo?.appVersion,
Expand Down Expand Up @@ -215,7 +216,6 @@ class DeviceAndroidInfo with Invokable {
'tags': () => androidInfo?.tags,
'type': () => androidInfo?.type,
'isPhysicalDevice': () => androidInfo?.isPhysicalDevice,
'serialNumber': () => androidInfo?.serialNumber,
};
}

Expand Down
Loading
Loading