diff --git a/.github/workflows/all_plugins.yaml b/.github/workflows/all_plugins.yaml
index f0d6cc2b..fa618da7 100644
--- a/.github/workflows/all_plugins.yaml
+++ b/.github/workflows/all_plugins.yaml
@@ -49,6 +49,28 @@ jobs:
- name: 'Flutter Pub Get'
run: melos run get
+ # Runs on Linux, so this is also what catches paths whose casing only works
+ # on a case-insensitive filesystem — the analyze and format jobs never touch
+ # the platform directories or the asset bundle.
+ build_playground:
+ timeout-minutes: 30
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9
+ - uses: subosito/flutter-action@48cafc24713cca54bbe03cdc3a423187d413aafa
+ with:
+ channel: 'stable'
+ cache: true
+ - uses: bluefireteam/melos-action@dd3c344d731938d2ab2567a261f54a19a68b5f6a
+ with:
+ melos-version: '3.0.1'
+ - name: 'Build web'
+ run: flutter build web
+ working-directory: stac_playground
+ - name: 'Test'
+ run: flutter test
+ working-directory: stac_playground
+
format:
runs-on: ubuntu-latest
timeout-minutes: 20
diff --git a/.gitignore b/.gitignore
index fa6e531d..f1d42fc2 100644
--- a/.gitignore
+++ b/.gitignore
@@ -53,3 +53,4 @@ tools/stac-vscode/build/
*.vsix
.eslintcache
.vscode-test/
+.claude/
diff --git a/.vscode/launch.json b/.vscode/launch.json
index 5062f8f8..63b43669 100644
--- a/.vscode/launch.json
+++ b/.vscode/launch.json
@@ -5,8 +5,8 @@
"version": "0.2.0",
"configurations": [
{
- "name": "stac_gallery",
- "cwd": "examples/stac_gallery",
+ "name": "stac_playground",
+ "cwd": "stac_playground",
"request": "launch",
"type": "dart"
},
diff --git a/assets/playground.png b/assets/playground.png
index 0e698e17..d15f9a74 100644
Binary files a/assets/playground.png and b/assets/playground.png differ
diff --git a/docs/actions/multi_action.mdx b/docs/actions/multi_action.mdx
index 61630607..80919a6e 100644
--- a/docs/actions/multi_action.mdx
+++ b/docs/actions/multi_action.mdx
@@ -30,7 +30,7 @@ StacMultiAction(
),
behavior: SnackBarBehavior.floating,
),
- StacNetworkRequestAction(
+ StacNetworkRequest(
url: 'https://example.com/api',
method: Method.get,
queryParameters: {'page': 1},
diff --git a/docs/actions/set_value.mdx b/docs/actions/set_value.mdx
index aef5a289..225ecb36 100644
--- a/docs/actions/set_value.mdx
+++ b/docs/actions/set_value.mdx
@@ -125,7 +125,7 @@ StacMultiAction(
{'key': 'isLoading', 'value': true},
],
),
- StacNetworkRequestAction(
+ StacNetworkRequest(
url: 'https://api.example.com/data',
method: Method.get,
results: [
diff --git a/docs/widgets/form.mdx b/docs/widgets/form.mdx
index 7f40f676..26029476 100644
--- a/docs/widgets/form.mdx
+++ b/docs/widgets/form.mdx
@@ -87,7 +87,7 @@ StacForm(
StacFilledButton(
child: StacText(data: 'Submit'),
onPressed: StacFormValidateAction(
- isValid: StacNetworkRequestAction(
+ isValid: StacNetworkRequest(
url: 'https://dummyjson.com/auth/login',
method: Method.post,
contentType: 'application/json',
diff --git a/docs/widgets/refresh_indicator.mdx b/docs/widgets/refresh_indicator.mdx
index 01b42bb4..7a423387 100644
--- a/docs/widgets/refresh_indicator.mdx
+++ b/docs/widgets/refresh_indicator.mdx
@@ -8,8 +8,8 @@ import { PLAYGROUND_BASE_URL } from "/snippets/playground_base.mdx";
export const refreshIndicatorPreviewJson = {
"type": "refreshIndicator",
"onRefresh": {
- "actionType": "request",
- "url": "https://raw.githubusercontent.com/StacDev/stac/main/examples/stac_gallery/assets/json/list_view_example.json",
+ "actionType": "networkRequest",
+ "url": "https://raw.githubusercontent.com/StacDev/stac/main/stac_playground/assets/json/list_view_example.json",
"method": "get",
"contentType": "application/json"
},
@@ -74,8 +74,8 @@ To know more about the RefreshIndicator widget in Flutter, refer to the [officia
```dart
StacRefreshIndicator(
- onRefresh: StacNetworkRequestAction(
- url: 'https://raw.githubusercontent.com/StacDev/stac/main/examples/stac_gallery/assets/json/list_view_example.json',
+ onRefresh: StacNetworkRequest(
+ url: 'https://raw.githubusercontent.com/StacDev/stac/main/stac_playground/assets/json/list_view_example.json',
method: Method.get,
contentType: 'application/json',
),
@@ -97,8 +97,8 @@ StacRefreshIndicator(
{
"type": "refreshIndicator",
"onRefresh": {
- "actionType": "request",
- "url": "https://raw.githubusercontent.com/StacDev/stac/main/examples/stac_gallery/assets/json/list_view_example.json",
+ "actionType": "networkRequest",
+ "url": "https://raw.githubusercontent.com/StacDev/stac/main/stac_playground/assets/json/list_view_example.json",
"method": "get",
"contentType": "application/json"
},
diff --git a/examples/stac_gallery/README.md b/examples/stac_gallery/README.md
deleted file mode 100644
index 1f87114a..00000000
--- a/examples/stac_gallery/README.md
+++ /dev/null
@@ -1,25 +0,0 @@
-# Stac Gallery
-
-A gallery application built to showcase all the widgets and components available in the Stac Server-Driven UI framework. It acts as a live interactive catalog for developers.
-
-## What this demonstrates
-
-- Real-world usage of every built-in Stac widget.
-- Action handlers (navigation, dialogs, form submissions).
-- Theming and responsive layouts purely driven by JSON.
-
-## How to run
-
-1. Ensure you have Flutter installed.
-2. Navigate to this directory:
- ```bash
- cd examples/stac_gallery
- ```
-3. Get dependencies:
- ```bash
- flutter pub get
- ```
-4. Run the app:
- ```bash
- flutter run
- ```
diff --git a/examples/stac_gallery/analysis_options.yaml b/examples/stac_gallery/analysis_options.yaml
deleted file mode 100644
index 14b6d039..00000000
--- a/examples/stac_gallery/analysis_options.yaml
+++ /dev/null
@@ -1,6 +0,0 @@
-include: package:flutter_lints/flutter.yaml
-
-analyzer:
- exclude:
- - lib/**.g.dart
- - lib/**.freezed.dart
\ No newline at end of file
diff --git a/examples/stac_gallery/assets/images/example_image_1.jpeg b/examples/stac_gallery/assets/images/example_image_1.jpeg
deleted file mode 100644
index 532bb4ec..00000000
Binary files a/examples/stac_gallery/assets/images/example_image_1.jpeg and /dev/null differ
diff --git a/examples/stac_gallery/assets/images/example_image_2.jpeg b/examples/stac_gallery/assets/images/example_image_2.jpeg
deleted file mode 100644
index c4e63beb..00000000
Binary files a/examples/stac_gallery/assets/images/example_image_2.jpeg and /dev/null differ
diff --git a/examples/stac_gallery/assets/images/gallery.svg b/examples/stac_gallery/assets/images/gallery.svg
deleted file mode 100644
index d1e9c59b..00000000
--- a/examples/stac_gallery/assets/images/gallery.svg
+++ /dev/null
@@ -1,19 +0,0 @@
-
-
-
\ No newline at end of file
diff --git a/examples/stac_gallery/devtools_options.yaml b/examples/stac_gallery/devtools_options.yaml
deleted file mode 100644
index 7e7e7f67..00000000
--- a/examples/stac_gallery/devtools_options.yaml
+++ /dev/null
@@ -1 +0,0 @@
-extensions:
diff --git a/examples/stac_gallery/ios/Runner/AppDelegate.swift b/examples/stac_gallery/ios/Runner/AppDelegate.swift
deleted file mode 100644
index 793cbed9..00000000
--- a/examples/stac_gallery/ios/Runner/AppDelegate.swift
+++ /dev/null
@@ -1,13 +0,0 @@
-import UIKit
-import Flutter
-
-@main
-@objc class AppDelegate: FlutterAppDelegate {
- override func application(
- _ application: UIApplication,
- didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
- ) -> Bool {
- GeneratedPluginRegistrant.register(with: self)
- return super.application(application, didFinishLaunchingWithOptions: launchOptions)
- }
-}
diff --git a/examples/stac_gallery/ios/RunnerTests/RunnerTests.swift b/examples/stac_gallery/ios/RunnerTests/RunnerTests.swift
deleted file mode 100644
index 9ff9105c..00000000
--- a/examples/stac_gallery/ios/RunnerTests/RunnerTests.swift
+++ /dev/null
@@ -1,12 +0,0 @@
-import Flutter
-import UIKit
-import XCTest
-
-class RunnerTests: XCTestCase {
-
- func testExample() {
- // If you add code to the Runner application, consider adding tests here.
- // See https://developer.apple.com/documentation/xctest for more information about using XCTest.
- }
-
-}
diff --git a/examples/stac_gallery/lib/app/details/details_screen.dart b/examples/stac_gallery/lib/app/details/details_screen.dart
deleted file mode 100644
index 9b6b3258..00000000
--- a/examples/stac_gallery/lib/app/details/details_screen.dart
+++ /dev/null
@@ -1,17 +0,0 @@
-import 'package:flutter/material.dart';
-
-class DetailsScreen extends StatelessWidget {
- const DetailsScreen({super.key});
-
- @override
- Widget build(BuildContext context) {
- return Scaffold(
- appBar: AppBar(
- title: const Text('Details'),
- ),
- body: const Center(
- child: Text('This is a flutter widget created in example app.'),
- ),
- );
- }
-}
diff --git a/examples/stac_gallery/lib/app/example/example_screen.dart b/examples/stac_gallery/lib/app/example/example_screen.dart
deleted file mode 100644
index 2d1f3d24..00000000
--- a/examples/stac_gallery/lib/app/example/example_screen.dart
+++ /dev/null
@@ -1,14 +0,0 @@
-import 'package:freezed_annotation/freezed_annotation.dart';
-
-part 'example_screen.freezed.dart';
-part 'example_screen.g.dart';
-
-@freezed
-abstract class ExampleScreen with _$ExampleScreen {
- const factory ExampleScreen({
- required String assetPath,
- }) = _ExampleScreen;
-
- factory ExampleScreen.fromJson(Map json) =>
- _$ExampleScreenFromJson(json);
-}
diff --git a/examples/stac_gallery/lib/app/example/example_screen.freezed.dart b/examples/stac_gallery/lib/app/example/example_screen.freezed.dart
deleted file mode 100644
index e3c3330f..00000000
--- a/examples/stac_gallery/lib/app/example/example_screen.freezed.dart
+++ /dev/null
@@ -1,318 +0,0 @@
-// GENERATED CODE - DO NOT MODIFY BY HAND
-// coverage:ignore-file
-// ignore_for_file: type=lint
-// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
-
-part of 'example_screen.dart';
-
-// **************************************************************************
-// FreezedGenerator
-// **************************************************************************
-
-// dart format off
-T _$identity(T value) => value;
-
-/// @nodoc
-mixin _$ExampleScreen {
- String get assetPath;
-
- /// Create a copy of ExampleScreen
- /// with the given fields replaced by the non-null parameter values.
- @JsonKey(includeFromJson: false, includeToJson: false)
- @pragma('vm:prefer-inline')
- $ExampleScreenCopyWith get copyWith =>
- _$ExampleScreenCopyWithImpl(
- this as ExampleScreen, _$identity);
-
- /// Serializes this ExampleScreen to a JSON map.
- Map toJson();
-
- @override
- bool operator ==(Object other) {
- return identical(this, other) ||
- (other.runtimeType == runtimeType &&
- other is ExampleScreen &&
- (identical(other.assetPath, assetPath) ||
- other.assetPath == assetPath));
- }
-
- @JsonKey(includeFromJson: false, includeToJson: false)
- @override
- int get hashCode => Object.hash(runtimeType, assetPath);
-
- @override
- String toString() {
- return 'ExampleScreen(assetPath: $assetPath)';
- }
-}
-
-/// @nodoc
-abstract mixin class $ExampleScreenCopyWith<$Res> {
- factory $ExampleScreenCopyWith(
- ExampleScreen value, $Res Function(ExampleScreen) _then) =
- _$ExampleScreenCopyWithImpl;
- @useResult
- $Res call({String assetPath});
-}
-
-/// @nodoc
-class _$ExampleScreenCopyWithImpl<$Res>
- implements $ExampleScreenCopyWith<$Res> {
- _$ExampleScreenCopyWithImpl(this._self, this._then);
-
- final ExampleScreen _self;
- final $Res Function(ExampleScreen) _then;
-
- /// Create a copy of ExampleScreen
- /// with the given fields replaced by the non-null parameter values.
- @pragma('vm:prefer-inline')
- @override
- $Res call({
- Object? assetPath = null,
- }) {
- return _then(_self.copyWith(
- assetPath: null == assetPath
- ? _self.assetPath
- : assetPath // ignore: cast_nullable_to_non_nullable
- as String,
- ));
- }
-}
-
-/// Adds pattern-matching-related methods to [ExampleScreen].
-extension ExampleScreenPatterns on ExampleScreen {
- /// A variant of `map` that fallback to returning `orElse`.
- ///
- /// It is equivalent to doing:
- /// ```dart
- /// switch (sealedClass) {
- /// case final Subclass value:
- /// return ...;
- /// case _:
- /// return orElse();
- /// }
- /// ```
-
- @optionalTypeArgs
- TResult maybeMap(
- TResult Function(_ExampleScreen value)? $default, {
- required TResult orElse(),
- }) {
- final _that = this;
- switch (_that) {
- case _ExampleScreen() when $default != null:
- return $default(_that);
- case _:
- return orElse();
- }
- }
-
- /// A `switch`-like method, using callbacks.
- ///
- /// Callbacks receives the raw object, upcasted.
- /// It is equivalent to doing:
- /// ```dart
- /// switch (sealedClass) {
- /// case final Subclass value:
- /// return ...;
- /// case final Subclass2 value:
- /// return ...;
- /// }
- /// ```
-
- @optionalTypeArgs
- TResult map(
- TResult Function(_ExampleScreen value) $default,
- ) {
- final _that = this;
- switch (_that) {
- case _ExampleScreen():
- return $default(_that);
- case _:
- throw StateError('Unexpected subclass');
- }
- }
-
- /// A variant of `map` that fallback to returning `null`.
- ///
- /// It is equivalent to doing:
- /// ```dart
- /// switch (sealedClass) {
- /// case final Subclass value:
- /// return ...;
- /// case _:
- /// return null;
- /// }
- /// ```
-
- @optionalTypeArgs
- TResult? mapOrNull(
- TResult? Function(_ExampleScreen value)? $default,
- ) {
- final _that = this;
- switch (_that) {
- case _ExampleScreen() when $default != null:
- return $default(_that);
- case _:
- return null;
- }
- }
-
- /// A variant of `when` that fallback to an `orElse` callback.
- ///
- /// It is equivalent to doing:
- /// ```dart
- /// switch (sealedClass) {
- /// case Subclass(:final field):
- /// return ...;
- /// case _:
- /// return orElse();
- /// }
- /// ```
-
- @optionalTypeArgs
- TResult maybeWhen(
- TResult Function(String assetPath)? $default, {
- required TResult orElse(),
- }) {
- final _that = this;
- switch (_that) {
- case _ExampleScreen() when $default != null:
- return $default(_that.assetPath);
- case _:
- return orElse();
- }
- }
-
- /// A `switch`-like method, using callbacks.
- ///
- /// As opposed to `map`, this offers destructuring.
- /// It is equivalent to doing:
- /// ```dart
- /// switch (sealedClass) {
- /// case Subclass(:final field):
- /// return ...;
- /// case Subclass2(:final field2):
- /// return ...;
- /// }
- /// ```
-
- @optionalTypeArgs
- TResult when(
- TResult Function(String assetPath) $default,
- ) {
- final _that = this;
- switch (_that) {
- case _ExampleScreen():
- return $default(_that.assetPath);
- case _:
- throw StateError('Unexpected subclass');
- }
- }
-
- /// A variant of `when` that fallback to returning `null`
- ///
- /// It is equivalent to doing:
- /// ```dart
- /// switch (sealedClass) {
- /// case Subclass(:final field):
- /// return ...;
- /// case _:
- /// return null;
- /// }
- /// ```
-
- @optionalTypeArgs
- TResult? whenOrNull(
- TResult? Function(String assetPath)? $default,
- ) {
- final _that = this;
- switch (_that) {
- case _ExampleScreen() when $default != null:
- return $default(_that.assetPath);
- case _:
- return null;
- }
- }
-}
-
-/// @nodoc
-@JsonSerializable()
-class _ExampleScreen implements ExampleScreen {
- const _ExampleScreen({required this.assetPath});
- factory _ExampleScreen.fromJson(Map json) =>
- _$ExampleScreenFromJson(json);
-
- @override
- final String assetPath;
-
- /// Create a copy of ExampleScreen
- /// with the given fields replaced by the non-null parameter values.
- @override
- @JsonKey(includeFromJson: false, includeToJson: false)
- @pragma('vm:prefer-inline')
- _$ExampleScreenCopyWith<_ExampleScreen> get copyWith =>
- __$ExampleScreenCopyWithImpl<_ExampleScreen>(this, _$identity);
-
- @override
- Map toJson() {
- return _$ExampleScreenToJson(
- this,
- );
- }
-
- @override
- bool operator ==(Object other) {
- return identical(this, other) ||
- (other.runtimeType == runtimeType &&
- other is _ExampleScreen &&
- (identical(other.assetPath, assetPath) ||
- other.assetPath == assetPath));
- }
-
- @JsonKey(includeFromJson: false, includeToJson: false)
- @override
- int get hashCode => Object.hash(runtimeType, assetPath);
-
- @override
- String toString() {
- return 'ExampleScreen(assetPath: $assetPath)';
- }
-}
-
-/// @nodoc
-abstract mixin class _$ExampleScreenCopyWith<$Res>
- implements $ExampleScreenCopyWith<$Res> {
- factory _$ExampleScreenCopyWith(
- _ExampleScreen value, $Res Function(_ExampleScreen) _then) =
- __$ExampleScreenCopyWithImpl;
- @override
- @useResult
- $Res call({String assetPath});
-}
-
-/// @nodoc
-class __$ExampleScreenCopyWithImpl<$Res>
- implements _$ExampleScreenCopyWith<$Res> {
- __$ExampleScreenCopyWithImpl(this._self, this._then);
-
- final _ExampleScreen _self;
- final $Res Function(_ExampleScreen) _then;
-
- /// Create a copy of ExampleScreen
- /// with the given fields replaced by the non-null parameter values.
- @override
- @pragma('vm:prefer-inline')
- $Res call({
- Object? assetPath = null,
- }) {
- return _then(_ExampleScreen(
- assetPath: null == assetPath
- ? _self.assetPath
- : assetPath // ignore: cast_nullable_to_non_nullable
- as String,
- ));
- }
-}
-
-// dart format on
diff --git a/examples/stac_gallery/lib/app/example/example_screen.g.dart b/examples/stac_gallery/lib/app/example/example_screen.g.dart
deleted file mode 100644
index a5e5e9c1..00000000
--- a/examples/stac_gallery/lib/app/example/example_screen.g.dart
+++ /dev/null
@@ -1,17 +0,0 @@
-// GENERATED CODE - DO NOT MODIFY BY HAND
-
-part of 'example_screen.dart';
-
-// **************************************************************************
-// JsonSerializableGenerator
-// **************************************************************************
-
-_ExampleScreen _$ExampleScreenFromJson(Map json) =>
- _ExampleScreen(
- assetPath: json['assetPath'] as String,
- );
-
-Map _$ExampleScreenToJson(_ExampleScreen instance) =>
- {
- 'assetPath': instance.assetPath,
- };
diff --git a/examples/stac_gallery/lib/app/example/example_screen_parser.dart b/examples/stac_gallery/lib/app/example/example_screen_parser.dart
deleted file mode 100644
index ef3a14b0..00000000
--- a/examples/stac_gallery/lib/app/example/example_screen_parser.dart
+++ /dev/null
@@ -1,78 +0,0 @@
-import 'package:flutter/material.dart';
-import 'package:flutter_bloc/flutter_bloc.dart';
-import 'package:stac/stac.dart';
-import 'package:stac_gallery/app/example/example_screen.dart';
-import 'package:stac_gallery/app_theme/app_theme_cubit.dart';
-
-class ExampleScreenParser extends StacParser {
- const ExampleScreenParser();
-
- @override
- ExampleScreen getModel(Map json) =>
- ExampleScreen.fromJson(json);
-
- @override
- String get type => 'exampleScreen';
-
- @override
- Widget parse(BuildContext context, ExampleScreen model) {
- return BlocBuilder(
- builder: (context, state) {
- return Scaffold(
- appBar: AppBar(
- backgroundColor: Colors.transparent,
- elevation: 0,
- leading: IconButton(
- onPressed: () => Navigator.of(context).pop(),
- icon: Icon(Icons.arrow_back,
- color: Theme.of(context).colorScheme.onSurface),
- ),
- actions: [
- Row(
- children: [
- Text(
- 'Dark Mode',
- style: TextStyle(
- fontSize: 16,
- fontWeight: FontWeight.w400,
- color: Theme.of(context).colorScheme.onSurface,
- ),
- ),
- Switch(
- value: state.themeMode == ThemeMode.dark,
- activeThumbColor: Theme.of(context).colorScheme.primary,
- activeTrackColor: Theme.of(context).colorScheme.onSurface,
- inactiveTrackColor:
- Theme.of(context).colorScheme.primary.withAlpha(80),
- inactiveThumbColor: Theme.of(context).colorScheme.primary,
- onChanged: (bool enable) {
- context.read().enableDarkMode(enable);
- },
- ),
- ],
- )
- ],
- ),
- body: Container(
- margin: const EdgeInsets.fromLTRB(24, 16, 24, 24),
- clipBehavior: Clip.hardEdge,
- height: double.infinity,
- width: double.infinity,
- decoration: BoxDecoration(
- borderRadius: const BorderRadius.all(Radius.circular(12)),
- boxShadow: [
- BoxShadow(
- color: Colors.grey.withAlpha(128),
- spreadRadius: 5,
- blurRadius: 7,
- offset: const Offset(0, 3),
- ),
- ],
- ),
- child: Stac.fromAssets(model.assetPath),
- ),
- );
- },
- );
- }
-}
diff --git a/examples/stac_gallery/lib/app/home/cubit/home_cubit.dart b/examples/stac_gallery/lib/app/home/cubit/home_cubit.dart
deleted file mode 100644
index 3e901c2e..00000000
--- a/examples/stac_gallery/lib/app/home/cubit/home_cubit.dart
+++ /dev/null
@@ -1,72 +0,0 @@
-import 'dart:convert';
-
-import 'package:flutter/material.dart';
-import 'package:flutter/services.dart';
-import 'package:flutter_bloc/flutter_bloc.dart';
-
-part 'home_state.dart';
-
-class HomeBloc extends Cubit {
- HomeBloc() : super(const HomeLoading()) {
- controller = TextEditingController()
- ..addListener(_textEditingControllerListener);
- _initialize();
- }
-
- late final TextEditingController controller;
-
- final List