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
26 changes: 26 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,32 @@ jobs:
- name: Run tests
run: flutter test

test_web:
name: Run tests on web
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3

- name: Setup Chrome
id: setup-chrome
uses: browser-actions/setup-chrome@v1
with:
install-dependencies: true

- name: Setup Flutter
uses: subosito/flutter-action@v2
with:
channel: 'stable'

- name: Install dependencies
run: flutter pub get

# Guards web support: a `dart:io` import in lib/ fails to compile here.
- name: Run tests
run: flutter test --platform chrome
env:
CHROME_EXECUTABLE: ${{ steps.setup-chrome.outputs.chrome-path }}

dry_run:
name: Publish dry run
runs-on: ubuntu-latest
Expand Down
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,12 @@ exhaustive `switch` statements over it need a `Cancelled` case added.
`map()` and `flatMap()` pass cancellation through
* Note: the task is not interrupted on cancellation — Dart futures cannot be
cancelled, so it runs to completion and its result is discarded
* Added Flutter Web support — `flutter_future_progress_dialog` no longer imports
`dart:io`, so pub.dev now reports all six platforms
* **Behaviour change:** `showAdaptiveProgressDialog` now selects its style from
`Theme.of(context).platform` instead of the host operating system, matching
Flutter's own `showAdaptiveDialog`. An app that sets `ThemeData(platform: ...)`
now gets the style it asked for rather than the style of the machine it runs on

## 1.5.0

Expand Down
11 changes: 6 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ Supported platforms, as reported on [pub.dev](https://pub.dev/packages/flutter_f
| macOS | Yes |
| Windows | Yes |
| Linux | Yes |
| Web | No — `flutter_future_progress_dialog` imports `dart:io` for the platform check in `showAdaptiveProgressDialog` |
| Web | Yes |

## Installation

Expand Down Expand Up @@ -160,6 +160,8 @@ Note that `future` takes a *callback* returning a `Future`, not a `Future` itsel

Call `showCupertinoProgressDialog` for an iOS-styled dialog built around `CupertinoActivityIndicator`, or `showAdaptiveProgressDialog` to select the style automatically — Cupertino on iOS and macOS, Material everywhere else.

`showAdaptiveProgressDialog` reads `Theme.of(context).platform`, the same signal Flutter's own `showAdaptiveDialog` uses. An app that overrides `ThemeData.platform` gets the style it asked for, and the selection works on Flutter Web.

```dart
// Always iOS style
final result = await showCupertinoProgressDialog(
Expand Down Expand Up @@ -257,7 +259,7 @@ final result = await showProgressDialog(
|---|---|---|
| `showProgressDialog<T>` | Material — `Dialog` with a `CircularProgressIndicator` | `Future<ProgressDialogResult<T>>` |
| `showCupertinoProgressDialog<T>` | Cupertino — `CupertinoPopupSurface` with a `CupertinoActivityIndicator` | `Future<ProgressDialogResult<T>>` |
| `showAdaptiveProgressDialog<T>` | Cupertino on iOS and macOS, Material elsewhere | `Future<ProgressDialogResult<T>>` |
| `showAdaptiveProgressDialog<T>` | Cupertino on iOS and macOS, Material elsewhere, per `Theme.of(context).platform` | `Future<ProgressDialogResult<T>>` |

### Parameters

Expand Down Expand Up @@ -306,11 +308,11 @@ Yes. A `Future<void>` task yields `Success<void>`, and a task returning `null` y

### Does flutter_future_progress_dialog support Flutter Web?

No. `flutter_future_progress_dialog` imports `dart:io` to detect the platform in `showAdaptiveProgressDialog`, so pub.dev lists Android, iOS, macOS, Windows, and Linux, but not Web.
Yes. `flutter_future_progress_dialog` supports all six Flutter platforms — Android, iOS, macOS, Windows, Linux, and Web. Platform detection in `showAdaptiveProgressDialog` goes through `Theme.of(context).platform` rather than `dart:io`, so nothing is host-specific.

### What is the difference between the three functions?

`showProgressDialog` always renders a Material dialog, `showCupertinoProgressDialog` always renders an iOS-style dialog, and `showAdaptiveProgressDialog` picks between them based on the host platform. All three take the same task callback and return the same `ProgressDialogResult<T>`.
`showProgressDialog` always renders a Material dialog, `showCupertinoProgressDialog` always renders an iOS-style dialog, and `showAdaptiveProgressDialog` picks between them based on `Theme.of(context).platform`. All three take the same task callback and return the same `ProgressDialogResult<T>`.

### Do I need to check `context.mounted` after awaiting?

Expand All @@ -319,7 +321,6 @@ Yes, if you use the `BuildContext` afterwards. `showProgressDialog` closes its o
## Known limitations

- **The task cannot be aborted.** Cancellation dismisses the dialog, not the work. See [Can the user cancel the progress dialog?](#can-the-user-cancel-the-progress-dialog)
- **No Flutter Web support**, because of the `dart:io` import used for platform detection.
- **No determinate progress.** The dialog shows an indeterminate spinner; there is no percentage or step reporting.
- **If the hosting `Navigator` is disposed while the task is in flight** — for example a nested navigator removed from the widget tree — the dialog is torn down without error, but the returned future never completes, so the `await` waits forever.

Expand Down
9 changes: 6 additions & 3 deletions lib/src/show_progress_dialog.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import 'dart:io';

import 'package:flutter/cupertino.dart' as c;
import 'package:flutter/material.dart' as m;
import 'package:flutter/widgets.dart' as w;
Expand Down Expand Up @@ -191,6 +189,10 @@ Future<ProgressDialogResult<T>> showCupertinoProgressDialog<T>({
/// Displays a Cupertino-styled dialog on iOS and macOS, and a Material-styled
/// dialog on all other platforms.
///
/// The style is chosen from `Theme.of(context).platform`, matching Flutter's own
/// `showAdaptiveDialog`. An app that overrides `ThemeData.platform` therefore
/// gets the dialog style it asked for, and the choice works on Flutter Web.
///
/// Parameters:
/// * [context] - The build context used to show the dialog
/// * [future] - The async task to execute while showing the progress dialog
Expand Down Expand Up @@ -240,7 +242,8 @@ Future<ProgressDialogResult<T>> showAdaptiveProgressDialog<T>({
bool useSafeArea = true,
bool? requestFocus,
}) async {
if (Platform.isMacOS || Platform.isIOS) {
final platform = m.Theme.of(context).platform;
if (platform == m.TargetPlatform.iOS || platform == m.TargetPlatform.macOS) {
return await showCupertinoProgressDialog(
context: context,
future: future,
Expand Down
94 changes: 94 additions & 0 deletions test/progress_dialog_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -690,4 +690,98 @@ void main() {
);
});
});

// showAdaptiveProgressDialog must follow the target platform reported by the
// theme, the way Flutter's own showAdaptiveDialog does — not the host OS the
// process happens to be running on.
group('adaptive platform selection', () {
Future<Completer<void>> pumpAdaptive(WidgetTester tester, TargetPlatform platform) async {
final task = Completer<void>();

await tester.pumpWidget(
MaterialApp(
theme: ThemeData(platform: platform),
home: Material(
child: Builder(
builder: (context) {
SchedulerBinding.instance.addPostFrameCallback((_) {
showAdaptiveProgressDialog<void>(
context: context,
future: () => task.future,
).ignore();
});
return Container();
},
),
),
),
);
await tester.pump();
await tester.pump();
await tester.pump(const Duration(milliseconds: 300));

return task;
}

for (final platform in [TargetPlatform.iOS, TargetPlatform.macOS]) {
testWidgets('uses a Cupertino dialog on $platform', (WidgetTester tester) async {
final task = await pumpAdaptive(tester, platform);

expect(find.byType(CupertinoActivityIndicator), findsOneWidget);
expect(find.byType(CircularProgressIndicator), findsNothing);

task.complete();
await tester.pumpAndSettle();
expect(tester.takeException(), isNull);
});
}

for (final platform in [
TargetPlatform.android,
TargetPlatform.fuchsia,
TargetPlatform.linux,
TargetPlatform.windows,
]) {
testWidgets('uses a Material dialog on $platform', (WidgetTester tester) async {
final task = await pumpAdaptive(tester, platform);

expect(find.byType(CircularProgressIndicator), findsOneWidget);
expect(find.byType(CupertinoActivityIndicator), findsNothing);

task.complete();
await tester.pumpAndSettle();
expect(tester.takeException(), isNull);
});
}

testWidgets('returns a result like the non-adaptive variants', (WidgetTester tester) async {
final completer = Completer<ProgressDialogResult<String>>();

await tester.pumpWidget(
MaterialApp(
theme: ThemeData(platform: TargetPlatform.android),
home: Material(
child: Builder(
builder: (context) {
SchedulerBinding.instance.addPostFrameCallback((_) {
showAdaptiveProgressDialog<String>(
context: context,
future: () async {
await Future<void>.delayed(const Duration(milliseconds: 100));
return 'ok';
},
).then(completer.complete);
});
return Container();
},
),
),
),
);
await tester.pumpAndSettle();

expect(await completer.future, const Success<String>('ok'));
expect(tester.takeException(), isNull);
});
});
}
Loading