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 @@ - - - - - gallery_grid_view [#1404] - Created with Sketch. - - - - - - - - - - - - - \ 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> _allItems = []; - - void _initialize() async { - final items = await _loadAndParseJsonFromAsset(); - if (items.isNotEmpty) { - _allItems.addAll(items); - } - emit(HomeLoaded(items: items)); - } - - void _onUserInput({required String keyword}) { - if (keyword.isNotEmpty) { - List> matchedItems = []; - - keyword = keyword.toLowerCase(); - for (final Map item in _allItems) { - var text = item["title"]?["data"]; - if (text != null && text is String) { - if (text.toLowerCase().contains(keyword)) { - matchedItems.add(item); - } - } - } - emit(HomeLoaded(items: matchedItems)); - } else { - emit(HomeLoaded(items: _allItems)); - } - } - - Future>> _loadAndParseJsonFromAsset() async { - try { - final String json = - await rootBundle.loadString("assets/json/home_screen.json"); - final List decodedJson = jsonDecode(json); - final List> castedJson = - decodedJson.cast>(); - return castedJson; - } catch (_) { - return const []; - } - } - - // Listen to text editing controller for the latest keyword - void _textEditingControllerListener() { - _onUserInput(keyword: controller.text); - } - - @override - Future close() { - controller - ..removeListener(_textEditingControllerListener) - ..dispose(); - return super.close(); - } -} diff --git a/examples/stac_gallery/lib/app/home/cubit/home_state.dart b/examples/stac_gallery/lib/app/home/cubit/home_state.dart deleted file mode 100644 index a0cfc1e0..00000000 --- a/examples/stac_gallery/lib/app/home/cubit/home_state.dart +++ /dev/null @@ -1,16 +0,0 @@ -part of 'home_cubit.dart'; - -@immutable -sealed class HomeState { - final List> items; - - const HomeState({required this.items}); -} - -class HomeLoading extends HomeState { - const HomeLoading({super.items = const []}); -} - -class HomeLoaded extends HomeState { - const HomeLoaded({required super.items}); -} diff --git a/examples/stac_gallery/lib/app/home/home_screen.dart b/examples/stac_gallery/lib/app/home/home_screen.dart deleted file mode 100644 index 006f38f2..00000000 --- a/examples/stac_gallery/lib/app/home/home_screen.dart +++ /dev/null @@ -1,67 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:stac/stac.dart'; - -import 'cubit/home_cubit.dart'; - -class HomeScreen extends StatelessWidget { - const HomeScreen({super.key}); - - @override - Widget build(BuildContext context) { - return BlocProvider( - create: (context) => HomeBloc(), - child: Builder( - builder: (context) { - return Scaffold( - appBar: AppBar( - title: const Text("Stac Gallery"), - ), - body: Column( - children: [ - TextField( - controller: BlocProvider.of(context).controller, - decoration: InputDecoration( - hintText: "Search", - prefixIcon: const Icon(Icons.search), - suffixIcon: IconButton( - onPressed: - BlocProvider.of(context).controller.clear, - icon: const Icon(Icons.clear), - ), - ), - ), - const SizedBox(height: 10.0), - Expanded( - child: BlocBuilder( - builder: (context, state) { - if (state is HomeLoaded) { - return Stac.fromJson( - { - "type": "listView", - "shrinkWrap": false, - "separator": { - "type": "container", - "height": 10 - }, - "children": state.items, - }, - context, - ) ?? - const SizedBox(); - } - if (state is HomeLoading) { - return const CircularProgressIndicator(); - } - return const SizedBox(); - }, - ), - ), - ], - ), - ); - }, - ), - ); - } -} diff --git a/examples/stac_gallery/lib/app_theme/app_theme_cubit.dart b/examples/stac_gallery/lib/app_theme/app_theme_cubit.dart deleted file mode 100644 index e5d5550f..00000000 --- a/examples/stac_gallery/lib/app_theme/app_theme_cubit.dart +++ /dev/null @@ -1,39 +0,0 @@ -import 'dart:convert'; - -import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:stac/stac.dart'; - -part 'app_theme_state.dart'; - -class AppThemeCubit extends Cubit { - AppThemeCubit() : super(const AppThemeState()); - - Future loadThemes() async { - await _loadThemes(); - } - - void enableDarkMode(bool enable) { - emit(state.copyWith(themeMode: enable ? ThemeMode.dark : ThemeMode.light)); - } - - Future _loadThemes() async { - final String lightThemeData = - await rootBundle.loadString('assets/json/example_light_theme.json'); - final Map lightThemeJsonData = jsonDecode(lightThemeData); - final lightTheme = StacTheme.fromJson(lightThemeJsonData); - - final String darkThemeData = - await rootBundle.loadString('assets/json/example_dark_theme.json'); - final Map darkThemeJsonData = jsonDecode(darkThemeData); - final darkTheme = StacTheme.fromJson(darkThemeJsonData); - - emit( - state.copyWith( - lightTheme: lightTheme, - darkTheme: darkTheme, - ), - ); - } -} diff --git a/examples/stac_gallery/lib/app_theme/app_theme_state.dart b/examples/stac_gallery/lib/app_theme/app_theme_state.dart deleted file mode 100644 index 15bdd018..00000000 --- a/examples/stac_gallery/lib/app_theme/app_theme_state.dart +++ /dev/null @@ -1,25 +0,0 @@ -part of 'app_theme_cubit.dart'; - -class AppThemeState { - const AppThemeState({ - this.themeMode = ThemeMode.light, - this.lightTheme, - this.darkTheme, - }); - - final ThemeMode themeMode; - final StacTheme? lightTheme; - final StacTheme? darkTheme; - - AppThemeState copyWith({ - ThemeMode? themeMode, - StacTheme? lightTheme, - StacTheme? darkTheme, - }) { - return AppThemeState( - themeMode: themeMode ?? this.themeMode, - lightTheme: lightTheme ?? this.lightTheme, - darkTheme: darkTheme ?? this.darkTheme, - ); - } -} diff --git a/examples/stac_gallery/lib/main.dart b/examples/stac_gallery/lib/main.dart deleted file mode 100644 index bdacecc7..00000000 --- a/examples/stac_gallery/lib/main.dart +++ /dev/null @@ -1,57 +0,0 @@ -import 'dart:io'; - -import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:stac/stac.dart'; -import 'package:stac_gallery/app/details/details_screen.dart'; -import 'package:stac_gallery/app/example/example_screen_parser.dart'; -import 'package:stac_gallery/app/home/home_screen.dart'; -import 'package:stac_gallery/app_theme/app_theme_cubit.dart'; -import 'package:stac_webview/stac_webview.dart'; - -class MyHttpOverrides extends HttpOverrides { - @override - HttpClient createHttpClient(SecurityContext? context) { - return super.createHttpClient(context) - ..badCertificateCallback = - (X509Certificate cert, String host, int port) => true; - } -} - -void main() async { - HttpOverrides.global = MyHttpOverrides(); - await Stac.initialize( - parsers: const [ - ExampleScreenParser(), - StacWebViewParser(), - ], - ); - - runApp(const MyApp()); -} - -class MyApp extends StatelessWidget { - const MyApp({super.key}); - - @override - Widget build(BuildContext context) { - return BlocProvider( - create: (context) => AppThemeCubit()..loadThemes(), - child: BlocBuilder( - builder: (context, state) { - return StacApp( - theme: StacAppTheme.json(payload: state.lightTheme), - darkTheme: StacAppTheme.json(payload: state.darkTheme), - themeMode: state.themeMode, - homeBuilder: (context) => HomeScreen(), - title: 'Stac Gallery', - routes: { - '/homeScreen': (context) => const HomeScreen(), - '/detailsScreen': (context) => const DetailsScreen(), - }, - ); - }, - ), - ); - } -} diff --git a/examples/stac_gallery/linux/flutter/generated_plugin_registrant.cc b/examples/stac_gallery/linux/flutter/generated_plugin_registrant.cc deleted file mode 100644 index e71a16d2..00000000 --- a/examples/stac_gallery/linux/flutter/generated_plugin_registrant.cc +++ /dev/null @@ -1,11 +0,0 @@ -// -// Generated file. Do not edit. -// - -// clang-format off - -#include "generated_plugin_registrant.h" - - -void fl_register_plugins(FlPluginRegistry* registry) { -} diff --git a/examples/stac_gallery/macos/Podfile.lock b/examples/stac_gallery/macos/Podfile.lock deleted file mode 100644 index edcac57e..00000000 --- a/examples/stac_gallery/macos/Podfile.lock +++ /dev/null @@ -1,23 +0,0 @@ -PODS: - - FlutterMacOS (1.0.0) - - webview_flutter_wkwebview (0.0.1): - - Flutter - - FlutterMacOS - -DEPENDENCIES: - - FlutterMacOS (from `Flutter/ephemeral`) - - webview_flutter_wkwebview (from `Flutter/ephemeral/.symlinks/plugins/webview_flutter_wkwebview/darwin`) - -EXTERNAL SOURCES: - FlutterMacOS: - :path: Flutter/ephemeral - webview_flutter_wkwebview: - :path: Flutter/ephemeral/.symlinks/plugins/webview_flutter_wkwebview/darwin - -SPEC CHECKSUMS: - FlutterMacOS: 8f6f14fa908a6fb3fba0cd85dbd81ec4b251fb24 - webview_flutter_wkwebview: 1821ceac936eba6f7984d89a9f3bcb4dea99ebb2 - -PODFILE CHECKSUM: 236401fc2c932af29a9fcf0e97baeeb2d750d367 - -COCOAPODS: 1.16.2 diff --git a/examples/stac_gallery/macos/Runner/AppDelegate.swift b/examples/stac_gallery/macos/Runner/AppDelegate.swift deleted file mode 100644 index 33b0f3ed..00000000 --- a/examples/stac_gallery/macos/Runner/AppDelegate.swift +++ /dev/null @@ -1,9 +0,0 @@ -import Cocoa -import FlutterMacOS - -@main -class AppDelegate: FlutterAppDelegate { - override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { - return true - } -} diff --git a/examples/stac_gallery/macos/Runner/MainFlutterWindow.swift b/examples/stac_gallery/macos/Runner/MainFlutterWindow.swift deleted file mode 100644 index 8612669b..00000000 --- a/examples/stac_gallery/macos/Runner/MainFlutterWindow.swift +++ /dev/null @@ -1,15 +0,0 @@ -import Cocoa -import FlutterMacOS - -class MainFlutterWindow: NSWindow { - override func awakeFromNib() { - let flutterViewController = FlutterViewController() - let windowFrame = self.frame - self.contentViewController = flutterViewController - self.setFrame(windowFrame, display: true) - - RegisterGeneratedPlugins(registry: flutterViewController) - - super.awakeFromNib() - } -} diff --git a/examples/stac_gallery/macos/RunnerTests/RunnerTests.swift b/examples/stac_gallery/macos/RunnerTests/RunnerTests.swift deleted file mode 100644 index d6c87c0e..00000000 --- a/examples/stac_gallery/macos/RunnerTests/RunnerTests.swift +++ /dev/null @@ -1,12 +0,0 @@ -import FlutterMacOS -import Cocoa -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/test/widget_test.dart b/examples/stac_gallery/test/widget_test.dart deleted file mode 100644 index 32026e73..00000000 --- a/examples/stac_gallery/test/widget_test.dart +++ /dev/null @@ -1,29 +0,0 @@ -// This is a basic Flutter widget test. -// -// To perform an interaction with a widget in your test, use the WidgetTester -// utility in the flutter_test package. For example, you can send tap and scroll -// gestures. You can also use WidgetTester to find child widgets in the widget -// tree, read text, and verify that the values of widget properties are correct. - -import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:stac_gallery/main.dart'; - -void main() { - testWidgets('Counter increments smoke test', (WidgetTester tester) async { - // Build our app and trigger a frame. - await tester.pumpWidget(const MyApp()); - - // Verify that our counter starts at 0. - expect(find.text('0'), findsOneWidget); - expect(find.text('1'), findsNothing); - - // Tap the '+' icon and trigger a frame. - await tester.tap(find.byIcon(Icons.add)); - await tester.pump(); - - // Verify that our counter has incremented. - expect(find.text('0'), findsNothing); - expect(find.text('1'), findsOneWidget); - }); -} diff --git a/examples/stac_gallery/web/favicon.png b/examples/stac_gallery/web/favicon.png deleted file mode 100644 index 8aaa46ac..00000000 Binary files a/examples/stac_gallery/web/favicon.png and /dev/null differ diff --git a/examples/stac_gallery/web/icons/Icon-192.png b/examples/stac_gallery/web/icons/Icon-192.png deleted file mode 100644 index b749bfef..00000000 Binary files a/examples/stac_gallery/web/icons/Icon-192.png and /dev/null differ diff --git a/examples/stac_gallery/web/icons/Icon-512.png b/examples/stac_gallery/web/icons/Icon-512.png deleted file mode 100644 index 88cfd48d..00000000 Binary files a/examples/stac_gallery/web/icons/Icon-512.png and /dev/null differ diff --git a/examples/stac_gallery/web/icons/Icon-maskable-192.png b/examples/stac_gallery/web/icons/Icon-maskable-192.png deleted file mode 100644 index eb9b4d76..00000000 Binary files a/examples/stac_gallery/web/icons/Icon-maskable-192.png and /dev/null differ diff --git a/examples/stac_gallery/web/icons/Icon-maskable-512.png b/examples/stac_gallery/web/icons/Icon-maskable-512.png deleted file mode 100644 index d69c5669..00000000 Binary files a/examples/stac_gallery/web/icons/Icon-maskable-512.png and /dev/null differ diff --git a/examples/stac_gallery/web/index.html b/examples/stac_gallery/web/index.html deleted file mode 100644 index 07d6b58b..00000000 --- a/examples/stac_gallery/web/index.html +++ /dev/null @@ -1,59 +0,0 @@ - - - - - - - - - - - - - - - - - - - - stac_gallery - - - - - - - - - - diff --git a/melos.yaml b/melos.yaml index d5ad2aa3..33dcd533 100644 --- a/melos.yaml +++ b/melos.yaml @@ -3,6 +3,7 @@ name: stac packages: - packages/* - examples/* + - stac_playground scripts: analyze: @@ -18,7 +19,7 @@ scripts: - melos exec --scope="stac_core" -- "dart run build_runner build --delete-conflicting-outputs" - melos exec --scope="stac" -- "dart run build_runner build --delete-conflicting-outputs" - melos exec --scope="stac_cli" -- "dart run build_runner build --delete-conflicting-outputs" - - melos exec --scope="counter_example" --scope="stac_gallery" --scope="stac_webview" -- "dart run build_runner build --delete-conflicting-outputs" + - melos exec --scope="counter_example" --scope="stac_webview" -- "dart run build_runner build --delete-conflicting-outputs" watch: exec: dart run build_runner watch --delete-conflicting-outputs packageFilters: diff --git a/packages/stac/example/README.md b/packages/stac/example/README.md index 1ca10976..3b797e3c 100644 --- a/packages/stac/example/README.md +++ b/packages/stac/example/README.md @@ -308,7 +308,7 @@ That's it with just few lines of code your SDUI app is up and running. ### More examples -Check out the [Stac Gallery](https://github.com/StacDev/stac/tree/main/examples/stac_gallery) app for more such examples. +Check out the [Stac Playground](https://github.com/StacDev/stac/tree/main/stac_playground) app for more such examples. [form_screen]: https://github.com/StacDev/stac/blob/main/assets/form_screen_image.png diff --git a/examples/stac_gallery/.gitignore b/stac_playground/.gitignore similarity index 98% rename from examples/stac_gallery/.gitignore rename to stac_playground/.gitignore index 62bad29b..4580f42e 100644 --- a/examples/stac_gallery/.gitignore +++ b/stac_playground/.gitignore @@ -29,10 +29,10 @@ migrate_working_dir/ .dart_tool/ .flutter-plugins .flutter-plugins-dependencies -.packages .pub-cache/ .pub/ /build/ +.firebase/ # Symbolication related app.*.symbols @@ -44,5 +44,3 @@ app.*.map.json /android/app/debug /android/app/profile /android/app/release - - diff --git a/examples/stac_gallery/.metadata b/stac_playground/.metadata similarity index 56% rename from examples/stac_gallery/.metadata rename to stac_playground/.metadata index 8af9f156..b02a7e4b 100644 --- a/examples/stac_gallery/.metadata +++ b/stac_playground/.metadata @@ -18,6 +18,21 @@ migration: - platform: android create_revision: 17025dd88227cd9532c33fa78f5250d548d87e9a base_revision: 17025dd88227cd9532c33fa78f5250d548d87e9a + - platform: ios + create_revision: 17025dd88227cd9532c33fa78f5250d548d87e9a + base_revision: 17025dd88227cd9532c33fa78f5250d548d87e9a + - platform: linux + create_revision: 17025dd88227cd9532c33fa78f5250d548d87e9a + base_revision: 17025dd88227cd9532c33fa78f5250d548d87e9a + - platform: macos + create_revision: 17025dd88227cd9532c33fa78f5250d548d87e9a + base_revision: 17025dd88227cd9532c33fa78f5250d548d87e9a + - platform: web + create_revision: 17025dd88227cd9532c33fa78f5250d548d87e9a + base_revision: 17025dd88227cd9532c33fa78f5250d548d87e9a + - platform: windows + create_revision: 17025dd88227cd9532c33fa78f5250d548d87e9a + base_revision: 17025dd88227cd9532c33fa78f5250d548d87e9a # User provided section diff --git a/stac_playground/README.md b/stac_playground/README.md new file mode 100644 index 00000000..348ccce8 --- /dev/null +++ b/stac_playground/README.md @@ -0,0 +1,16 @@ +# stac_playground + +Stac Playground + +## Getting Started + +This project is a starting point for a Flutter application. + +A few resources to get you started if this is your first Flutter project: + +- [Lab: Write your first Flutter app](https://docs.flutter.dev/get-started/codelab) +- [Cookbook: Useful Flutter samples](https://docs.flutter.dev/cookbook) + +For help getting started with Flutter development, view the +[online documentation](https://docs.flutter.dev/), which offers tutorials, +samples, guidance on mobile development, and a full API reference. diff --git a/stac_playground/analysis_options.yaml b/stac_playground/analysis_options.yaml new file mode 100644 index 00000000..4ededac3 --- /dev/null +++ b/stac_playground/analysis_options.yaml @@ -0,0 +1,34 @@ +# This file configures the analyzer, which statically analyzes Dart code to +# check for errors, warnings, and lints. +# +# The issues identified by the analyzer are surfaced in the UI of Dart-enabled +# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be +# invoked from the command line by running `flutter analyze`. + +# The following line activates a set of recommended lints for Flutter apps, +# packages, and plugins designed to encourage good coding practices. +include: package:flutter_lints/flutter.yaml + +analyzer: + exclude: + # `lib/dsl` ships as an asset bundle, so Flutter copies every example into + # build/. Without this, `dart analyze .` reports each one several times over. + - build/** + +linter: + # The lint rules applied to this project can be customized in the + # section below to disable rules from the `package:flutter_lints/flutter.yaml` + # included above or to enable additional rules. A list of all available lints + # and their documentation is published at https://dart.dev/lints. + # + # Instead of disabling a lint rule for the entire project in the + # section below, it can also be suppressed for a single line of code + # or a specific dart file by using the `// ignore: name_of_lint` and + # `// ignore_for_file: name_of_lint` syntax on the line or in the file + # producing the lint. + rules: + # avoid_print: false # Uncomment to disable the `avoid_print` rule + # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule + +# Additional information about this file can be found at +# https://dart.dev/guides/language/analysis-options diff --git a/examples/stac_gallery/android/.gitignore b/stac_playground/android/.gitignore similarity index 100% rename from examples/stac_gallery/android/.gitignore rename to stac_playground/android/.gitignore diff --git a/examples/stac_gallery/android/app/build.gradle b/stac_playground/android/app/build.gradle similarity index 93% rename from examples/stac_gallery/android/app/build.gradle rename to stac_playground/android/app/build.gradle index 7f7bdefd..beaff697 100644 --- a/examples/stac_gallery/android/app/build.gradle +++ b/stac_playground/android/app/build.gradle @@ -6,7 +6,7 @@ plugins { } android { - namespace = "com.stac.stac_gallery" + namespace = "com.stac.stac_playground" compileSdk = flutter.compileSdkVersion ndkVersion = flutter.ndkVersion @@ -21,7 +21,7 @@ android { defaultConfig { // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). - applicationId = "com.stac.stac_gallery" + applicationId = "com.stac.stac_playground" // You can update the following values to match your application needs. // For more information, see: https://flutter.dev/to/review-gradle-config. minSdk = flutter.minSdkVersion diff --git a/examples/stac_gallery/android/app/src/debug/AndroidManifest.xml b/stac_playground/android/app/src/debug/AndroidManifest.xml similarity index 100% rename from examples/stac_gallery/android/app/src/debug/AndroidManifest.xml rename to stac_playground/android/app/src/debug/AndroidManifest.xml diff --git a/examples/stac_gallery/android/app/src/main/AndroidManifest.xml b/stac_playground/android/app/src/main/AndroidManifest.xml similarity index 98% rename from examples/stac_gallery/android/app/src/main/AndroidManifest.xml rename to stac_playground/android/app/src/main/AndroidManifest.xml index 8af0c43d..96328179 100644 --- a/examples/stac_gallery/android/app/src/main/AndroidManifest.xml +++ b/stac_playground/android/app/src/main/AndroidManifest.xml @@ -1,6 +1,6 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/stac_playground/assets/images/logo_console.png b/stac_playground/assets/images/logo_console.png new file mode 100644 index 00000000..28fabc20 Binary files /dev/null and b/stac_playground/assets/images/logo_console.png differ diff --git a/examples/stac_gallery/assets/json/align_example.json b/stac_playground/assets/json/align_example.json similarity index 100% rename from examples/stac_gallery/assets/json/align_example.json rename to stac_playground/assets/json/align_example.json diff --git a/examples/stac_gallery/assets/json/app_bar_example.json b/stac_playground/assets/json/app_bar_example.json similarity index 100% rename from examples/stac_gallery/assets/json/app_bar_example.json rename to stac_playground/assets/json/app_bar_example.json diff --git a/examples/stac_gallery/assets/json/aspect_ratio_example.json b/stac_playground/assets/json/aspect_ratio_example.json similarity index 100% rename from examples/stac_gallery/assets/json/aspect_ratio_example.json rename to stac_playground/assets/json/aspect_ratio_example.json diff --git a/examples/stac_gallery/assets/json/auto_complete_example.json b/stac_playground/assets/json/auto_complete_example.json similarity index 100% rename from examples/stac_gallery/assets/json/auto_complete_example.json rename to stac_playground/assets/json/auto_complete_example.json diff --git a/examples/stac_gallery/assets/json/backdrop_filter_example.json b/stac_playground/assets/json/backdrop_filter_example.json similarity index 100% rename from examples/stac_gallery/assets/json/backdrop_filter_example.json rename to stac_playground/assets/json/backdrop_filter_example.json diff --git a/examples/stac_gallery/assets/json/badge_example.json b/stac_playground/assets/json/badge_example.json similarity index 100% rename from examples/stac_gallery/assets/json/badge_example.json rename to stac_playground/assets/json/badge_example.json diff --git a/examples/stac_gallery/assets/json/bottom_nav_bar_example.json b/stac_playground/assets/json/bottom_nav_bar_example.json similarity index 100% rename from examples/stac_gallery/assets/json/bottom_nav_bar_example.json rename to stac_playground/assets/json/bottom_nav_bar_example.json diff --git a/examples/stac_gallery/assets/json/bottom_sheet_example.json b/stac_playground/assets/json/bottom_sheet_example.json similarity index 100% rename from examples/stac_gallery/assets/json/bottom_sheet_example.json rename to stac_playground/assets/json/bottom_sheet_example.json diff --git a/examples/stac_gallery/assets/json/card_example.json b/stac_playground/assets/json/card_example.json similarity index 100% rename from examples/stac_gallery/assets/json/card_example.json rename to stac_playground/assets/json/card_example.json diff --git a/examples/stac_gallery/assets/json/carousel_view_example.json b/stac_playground/assets/json/carousel_view_example.json similarity index 100% rename from examples/stac_gallery/assets/json/carousel_view_example.json rename to stac_playground/assets/json/carousel_view_example.json diff --git a/examples/stac_gallery/assets/json/center_example.json b/stac_playground/assets/json/center_example.json similarity index 100% rename from examples/stac_gallery/assets/json/center_example.json rename to stac_playground/assets/json/center_example.json diff --git a/examples/stac_gallery/assets/json/chip_example.json b/stac_playground/assets/json/chip_example.json similarity index 100% rename from examples/stac_gallery/assets/json/chip_example.json rename to stac_playground/assets/json/chip_example.json diff --git a/examples/stac_gallery/assets/json/circular_progress_indicator_example.json b/stac_playground/assets/json/circular_progress_indicator_example.json similarity index 100% rename from examples/stac_gallery/assets/json/circular_progress_indicator_example.json rename to stac_playground/assets/json/circular_progress_indicator_example.json diff --git a/examples/stac_gallery/assets/json/clip_oval_example.json b/stac_playground/assets/json/clip_oval_example.json similarity index 100% rename from examples/stac_gallery/assets/json/clip_oval_example.json rename to stac_playground/assets/json/clip_oval_example.json diff --git a/examples/stac_gallery/assets/json/column_example.json b/stac_playground/assets/json/column_example.json similarity index 100% rename from examples/stac_gallery/assets/json/column_example.json rename to stac_playground/assets/json/column_example.json diff --git a/examples/stac_gallery/assets/json/conditional_example.json b/stac_playground/assets/json/conditional_example.json similarity index 100% rename from examples/stac_gallery/assets/json/conditional_example.json rename to stac_playground/assets/json/conditional_example.json diff --git a/examples/stac_gallery/assets/json/container_example.json b/stac_playground/assets/json/container_example.json similarity index 100% rename from examples/stac_gallery/assets/json/container_example.json rename to stac_playground/assets/json/container_example.json diff --git a/examples/stac_gallery/assets/json/dialog_example.json b/stac_playground/assets/json/dialog_example.json similarity index 100% rename from examples/stac_gallery/assets/json/dialog_example.json rename to stac_playground/assets/json/dialog_example.json diff --git a/examples/stac_gallery/assets/json/divider_example.json b/stac_playground/assets/json/divider_example.json similarity index 100% rename from examples/stac_gallery/assets/json/divider_example.json rename to stac_playground/assets/json/divider_example.json diff --git a/examples/stac_gallery/assets/json/drawer_example.json b/stac_playground/assets/json/drawer_example.json similarity index 100% rename from examples/stac_gallery/assets/json/drawer_example.json rename to stac_playground/assets/json/drawer_example.json diff --git a/examples/stac_gallery/assets/json/dropdown_menu_view_example.json b/stac_playground/assets/json/dropdown_menu_view_example.json similarity index 100% rename from examples/stac_gallery/assets/json/dropdown_menu_view_example.json rename to stac_playground/assets/json/dropdown_menu_view_example.json diff --git a/examples/stac_gallery/assets/json/dynamic_list_view_example.json b/stac_playground/assets/json/dynamic_list_view_example.json similarity index 100% rename from examples/stac_gallery/assets/json/dynamic_list_view_example.json rename to stac_playground/assets/json/dynamic_list_view_example.json diff --git a/examples/stac_gallery/assets/json/dynamic_view_example.json b/stac_playground/assets/json/dynamic_view_example.json similarity index 100% rename from examples/stac_gallery/assets/json/dynamic_view_example.json rename to stac_playground/assets/json/dynamic_view_example.json diff --git a/examples/stac_gallery/assets/json/elevated_button_example.json b/stac_playground/assets/json/elevated_button_example.json similarity index 100% rename from examples/stac_gallery/assets/json/elevated_button_example.json rename to stac_playground/assets/json/elevated_button_example.json diff --git a/examples/stac_gallery/assets/json/example_dark_theme.json b/stac_playground/assets/json/example_dark_theme.json similarity index 100% rename from examples/stac_gallery/assets/json/example_dark_theme.json rename to stac_playground/assets/json/example_dark_theme.json diff --git a/examples/stac_gallery/assets/json/example_light_theme.json b/stac_playground/assets/json/example_light_theme.json similarity index 100% rename from examples/stac_gallery/assets/json/example_light_theme.json rename to stac_playground/assets/json/example_light_theme.json diff --git a/examples/stac_gallery/assets/json/filled_button_example.json b/stac_playground/assets/json/filled_button_example.json similarity index 100% rename from examples/stac_gallery/assets/json/filled_button_example.json rename to stac_playground/assets/json/filled_button_example.json diff --git a/examples/stac_gallery/assets/json/fitted_box_example.json b/stac_playground/assets/json/fitted_box_example.json similarity index 100% rename from examples/stac_gallery/assets/json/fitted_box_example.json rename to stac_playground/assets/json/fitted_box_example.json diff --git a/examples/stac_gallery/assets/json/floating_action_button_example.json b/stac_playground/assets/json/floating_action_button_example.json similarity index 100% rename from examples/stac_gallery/assets/json/floating_action_button_example.json rename to stac_playground/assets/json/floating_action_button_example.json diff --git a/examples/stac_gallery/assets/json/form_example.json b/stac_playground/assets/json/form_example.json similarity index 100% rename from examples/stac_gallery/assets/json/form_example.json rename to stac_playground/assets/json/form_example.json diff --git a/examples/stac_gallery/assets/json/fractionally_sized_box_example.json b/stac_playground/assets/json/fractionally_sized_box_example.json similarity index 100% rename from examples/stac_gallery/assets/json/fractionally_sized_box_example.json rename to stac_playground/assets/json/fractionally_sized_box_example.json diff --git a/examples/stac_gallery/assets/json/grid_view_example.json b/stac_playground/assets/json/grid_view_example.json similarity index 100% rename from examples/stac_gallery/assets/json/grid_view_example.json rename to stac_playground/assets/json/grid_view_example.json diff --git a/examples/stac_gallery/assets/json/hero_example.json b/stac_playground/assets/json/hero_example.json similarity index 100% rename from examples/stac_gallery/assets/json/hero_example.json rename to stac_playground/assets/json/hero_example.json diff --git a/examples/stac_gallery/assets/json/home_screen.json b/stac_playground/assets/json/home_screen.json similarity index 100% rename from examples/stac_gallery/assets/json/home_screen.json rename to stac_playground/assets/json/home_screen.json diff --git a/examples/stac_gallery/assets/json/icon_button_example.json b/stac_playground/assets/json/icon_button_example.json similarity index 100% rename from examples/stac_gallery/assets/json/icon_button_example.json rename to stac_playground/assets/json/icon_button_example.json diff --git a/examples/stac_gallery/assets/json/icon_example.json b/stac_playground/assets/json/icon_example.json similarity index 100% rename from examples/stac_gallery/assets/json/icon_example.json rename to stac_playground/assets/json/icon_example.json diff --git a/examples/stac_gallery/assets/json/image_example.json b/stac_playground/assets/json/image_example.json similarity index 95% rename from examples/stac_gallery/assets/json/image_example.json rename to stac_playground/assets/json/image_example.json index 323f65e0..4f647d0c 100644 --- a/examples/stac_gallery/assets/json/image_example.json +++ b/stac_playground/assets/json/image_example.json @@ -46,7 +46,7 @@ }, { "type": "image", - "src": "assets/images/example_image_2.jpeg", + "src": "assets/images/logo_console.png", "imageType": "asset", "height": 150 } @@ -97,7 +97,7 @@ }, { "type": "image", - "src": "assets/images/example_image_1.jpeg", + "src": "assets/images/dart_logo.png", "imageType": "asset", "height": 150, "width": 100, @@ -127,7 +127,7 @@ { "type": "image", "imageType": "asset", - "src": "assets/images/gallery.svg", + "src": "assets/images/logo.svg", "color": "primary", "height": 100, "width": 100 diff --git a/examples/stac_gallery/assets/json/inkwell_example.json b/stac_playground/assets/json/inkwell_example.json similarity index 100% rename from examples/stac_gallery/assets/json/inkwell_example.json rename to stac_playground/assets/json/inkwell_example.json diff --git a/examples/stac_gallery/assets/json/limited_box_example.json b/stac_playground/assets/json/limited_box_example.json similarity index 100% rename from examples/stac_gallery/assets/json/limited_box_example.json rename to stac_playground/assets/json/limited_box_example.json diff --git a/examples/stac_gallery/assets/json/linear_progress_indicator_example.json b/stac_playground/assets/json/linear_progress_indicator_example.json similarity index 100% rename from examples/stac_gallery/assets/json/linear_progress_indicator_example.json rename to stac_playground/assets/json/linear_progress_indicator_example.json diff --git a/examples/stac_gallery/assets/json/list_tile_example.json b/stac_playground/assets/json/list_tile_example.json similarity index 100% rename from examples/stac_gallery/assets/json/list_tile_example.json rename to stac_playground/assets/json/list_tile_example.json diff --git a/examples/stac_gallery/assets/json/list_view_example.json b/stac_playground/assets/json/list_view_example.json similarity index 100% rename from examples/stac_gallery/assets/json/list_view_example.json rename to stac_playground/assets/json/list_view_example.json diff --git a/examples/stac_gallery/assets/json/navigation_bar_example.json b/stac_playground/assets/json/navigation_bar_example.json similarity index 100% rename from examples/stac_gallery/assets/json/navigation_bar_example.json rename to stac_playground/assets/json/navigation_bar_example.json diff --git a/examples/stac_gallery/assets/json/navigation_example.json b/stac_playground/assets/json/navigation_example.json similarity index 100% rename from examples/stac_gallery/assets/json/navigation_example.json rename to stac_playground/assets/json/navigation_example.json diff --git a/examples/stac_gallery/assets/json/opacity_example.json b/stac_playground/assets/json/opacity_example.json similarity index 100% rename from examples/stac_gallery/assets/json/opacity_example.json rename to stac_playground/assets/json/opacity_example.json diff --git a/examples/stac_gallery/assets/json/outlined_button_example.json b/stac_playground/assets/json/outlined_button_example.json similarity index 100% rename from examples/stac_gallery/assets/json/outlined_button_example.json rename to stac_playground/assets/json/outlined_button_example.json diff --git a/examples/stac_gallery/assets/json/padding_example.json b/stac_playground/assets/json/padding_example.json similarity index 100% rename from examples/stac_gallery/assets/json/padding_example.json rename to stac_playground/assets/json/padding_example.json diff --git a/examples/stac_gallery/assets/json/page_view_example.json b/stac_playground/assets/json/page_view_example.json similarity index 100% rename from examples/stac_gallery/assets/json/page_view_example.json rename to stac_playground/assets/json/page_view_example.json diff --git a/examples/stac_gallery/assets/json/placeholder_example.json b/stac_playground/assets/json/placeholder_example.json similarity index 100% rename from examples/stac_gallery/assets/json/placeholder_example.json rename to stac_playground/assets/json/placeholder_example.json diff --git a/examples/stac_gallery/assets/json/radio_example.json b/stac_playground/assets/json/radio_example.json similarity index 100% rename from examples/stac_gallery/assets/json/radio_example.json rename to stac_playground/assets/json/radio_example.json diff --git a/examples/stac_gallery/assets/json/refresh_indicator_example.json b/stac_playground/assets/json/refresh_indicator_example.json similarity index 77% rename from examples/stac_gallery/assets/json/refresh_indicator_example.json rename to stac_playground/assets/json/refresh_indicator_example.json index 7221f880..90ab2c2d 100644 --- a/examples/stac_gallery/assets/json/refresh_indicator_example.json +++ b/stac_playground/assets/json/refresh_indicator_example.json @@ -4,14 +4,14 @@ "type": "refreshIndicator", "onRefresh": { "actionType": "networkRequest", - "url": "https://raw.githubusercontent.com/StacDev/stac/main/examples/stac_gallery/assets/json/list_view_example.json", + "url": "https://raw.githubusercontent.com/StacDev/stac/main/stac_playground/assets/json/list_view_example.json", "method": "get", "contentType": "application/json" }, "child": { "type": "networkWidget", "request": { - "url": "https://raw.githubusercontent.com/StacDev/stac/main/examples/stac_gallery/assets/json/list_view_example.json", + "url": "https://raw.githubusercontent.com/StacDev/stac/main/stac_playground/assets/json/list_view_example.json", "method": "get" } } diff --git a/examples/stac_gallery/assets/json/row_example.json b/stac_playground/assets/json/row_example.json similarity index 100% rename from examples/stac_gallery/assets/json/row_example.json rename to stac_playground/assets/json/row_example.json diff --git a/examples/stac_gallery/assets/json/scaffold_example.json b/stac_playground/assets/json/scaffold_example.json similarity index 100% rename from examples/stac_gallery/assets/json/scaffold_example.json rename to stac_playground/assets/json/scaffold_example.json diff --git a/examples/stac_gallery/assets/json/scroll_view_example.json b/stac_playground/assets/json/scroll_view_example.json similarity index 100% rename from examples/stac_gallery/assets/json/scroll_view_example.json rename to stac_playground/assets/json/scroll_view_example.json diff --git a/examples/stac_gallery/assets/json/selectable_text_example.json b/stac_playground/assets/json/selectable_text_example.json similarity index 100% rename from examples/stac_gallery/assets/json/selectable_text_example.json rename to stac_playground/assets/json/selectable_text_example.json diff --git a/examples/stac_gallery/assets/json/sized_box_example.json b/stac_playground/assets/json/sized_box_example.json similarity index 100% rename from examples/stac_gallery/assets/json/sized_box_example.json rename to stac_playground/assets/json/sized_box_example.json diff --git a/examples/stac_gallery/assets/json/slider_example.json b/stac_playground/assets/json/slider_example.json similarity index 100% rename from examples/stac_gallery/assets/json/slider_example.json rename to stac_playground/assets/json/slider_example.json diff --git a/examples/stac_gallery/assets/json/sliver_app_bar_example.json b/stac_playground/assets/json/sliver_app_bar_example.json similarity index 100% rename from examples/stac_gallery/assets/json/sliver_app_bar_example.json rename to stac_playground/assets/json/sliver_app_bar_example.json diff --git a/examples/stac_gallery/assets/json/sliver_fill_remaining_example.json b/stac_playground/assets/json/sliver_fill_remaining_example.json similarity index 100% rename from examples/stac_gallery/assets/json/sliver_fill_remaining_example.json rename to stac_playground/assets/json/sliver_fill_remaining_example.json diff --git a/examples/stac_gallery/assets/json/sliver_grid_example.json b/stac_playground/assets/json/sliver_grid_example.json similarity index 100% rename from examples/stac_gallery/assets/json/sliver_grid_example.json rename to stac_playground/assets/json/sliver_grid_example.json diff --git a/examples/stac_gallery/assets/json/sliver_list_example.json b/stac_playground/assets/json/sliver_list_example.json similarity index 100% rename from examples/stac_gallery/assets/json/sliver_list_example.json rename to stac_playground/assets/json/sliver_list_example.json diff --git a/examples/stac_gallery/assets/json/sliver_opacity_example.json b/stac_playground/assets/json/sliver_opacity_example.json similarity index 100% rename from examples/stac_gallery/assets/json/sliver_opacity_example.json rename to stac_playground/assets/json/sliver_opacity_example.json diff --git a/examples/stac_gallery/assets/json/sliver_padding_example.json b/stac_playground/assets/json/sliver_padding_example.json similarity index 100% rename from examples/stac_gallery/assets/json/sliver_padding_example.json rename to stac_playground/assets/json/sliver_padding_example.json diff --git a/examples/stac_gallery/assets/json/sliver_safe_area_example.json b/stac_playground/assets/json/sliver_safe_area_example.json similarity index 100% rename from examples/stac_gallery/assets/json/sliver_safe_area_example.json rename to stac_playground/assets/json/sliver_safe_area_example.json diff --git a/examples/stac_gallery/assets/json/sliver_to_box_adapter_example.json b/stac_playground/assets/json/sliver_to_box_adapter_example.json similarity index 100% rename from examples/stac_gallery/assets/json/sliver_to_box_adapter_example.json rename to stac_playground/assets/json/sliver_to_box_adapter_example.json diff --git a/examples/stac_gallery/assets/json/sliver_visibility_example.json b/stac_playground/assets/json/sliver_visibility_example.json similarity index 100% rename from examples/stac_gallery/assets/json/sliver_visibility_example.json rename to stac_playground/assets/json/sliver_visibility_example.json diff --git a/stac_playground/assets/json/snackbar_example.json b/stac_playground/assets/json/snackbar_example.json new file mode 100644 index 00000000..c11df07c --- /dev/null +++ b/stac_playground/assets/json/snackbar_example.json @@ -0,0 +1,28 @@ +{ + "type": "scaffold", + "appBar": { + "type": "appBar", + "title": { + "type": "text", + "data": "SnackBar" + } + }, + "body": { + "type": "center", + "child": { + "type": "elevatedButton", + "child": { + "type": "text", + "data": "Show SnackBar" + }, + "onPressed": { + "actionType": "snackBar", + "content": { + "type": "text", + "data": "This is a Snackbar" + }, + "behavior": "floating" + } + } + } +} diff --git a/examples/stac_gallery/assets/json/spacer_example.json b/stac_playground/assets/json/spacer_example.json similarity index 100% rename from examples/stac_gallery/assets/json/spacer_example.json rename to stac_playground/assets/json/spacer_example.json diff --git a/examples/stac_gallery/assets/json/stack_example.json b/stac_playground/assets/json/stack_example.json similarity index 100% rename from examples/stac_gallery/assets/json/stack_example.json rename to stac_playground/assets/json/stack_example.json diff --git a/examples/stac_gallery/assets/json/switch_example.json b/stac_playground/assets/json/switch_example.json similarity index 100% rename from examples/stac_gallery/assets/json/switch_example.json rename to stac_playground/assets/json/switch_example.json diff --git a/examples/stac_gallery/assets/json/tab_bar_example.json b/stac_playground/assets/json/tab_bar_example.json similarity index 100% rename from examples/stac_gallery/assets/json/tab_bar_example.json rename to stac_playground/assets/json/tab_bar_example.json diff --git a/examples/stac_gallery/assets/json/table_example.json b/stac_playground/assets/json/table_example.json similarity index 100% rename from examples/stac_gallery/assets/json/table_example.json rename to stac_playground/assets/json/table_example.json diff --git a/examples/stac_gallery/assets/json/text_button_example.json b/stac_playground/assets/json/text_button_example.json similarity index 100% rename from examples/stac_gallery/assets/json/text_button_example.json rename to stac_playground/assets/json/text_button_example.json diff --git a/examples/stac_gallery/assets/json/text_example.json b/stac_playground/assets/json/text_example.json similarity index 100% rename from examples/stac_gallery/assets/json/text_example.json rename to stac_playground/assets/json/text_example.json diff --git a/examples/stac_gallery/assets/json/text_field_example.json b/stac_playground/assets/json/text_field_example.json similarity index 100% rename from examples/stac_gallery/assets/json/text_field_example.json rename to stac_playground/assets/json/text_field_example.json diff --git a/examples/stac_gallery/assets/json/tool_tip_example.json b/stac_playground/assets/json/tool_tip_example.json similarity index 100% rename from examples/stac_gallery/assets/json/tool_tip_example.json rename to stac_playground/assets/json/tool_tip_example.json diff --git a/examples/stac_gallery/assets/json/variable_example.json b/stac_playground/assets/json/variable_example.json similarity index 100% rename from examples/stac_gallery/assets/json/variable_example.json rename to stac_playground/assets/json/variable_example.json diff --git a/examples/stac_gallery/assets/json/variable_navigate_example.json b/stac_playground/assets/json/variable_navigate_example.json similarity index 100% rename from examples/stac_gallery/assets/json/variable_navigate_example.json rename to stac_playground/assets/json/variable_navigate_example.json diff --git a/examples/stac_gallery/assets/json/web_view_example.json b/stac_playground/assets/json/web_view_example.json similarity index 100% rename from examples/stac_gallery/assets/json/web_view_example.json rename to stac_playground/assets/json/web_view_example.json diff --git a/examples/stac_gallery/assets/json/wrap_example.json b/stac_playground/assets/json/wrap_example.json similarity index 100% rename from examples/stac_gallery/assets/json/wrap_example.json rename to stac_playground/assets/json/wrap_example.json diff --git a/stac_playground/firebase.json b/stac_playground/firebase.json new file mode 100644 index 00000000..66037326 --- /dev/null +++ b/stac_playground/firebase.json @@ -0,0 +1,16 @@ +{ + "hosting": { + "public": "build/web", + "ignore": [ + "firebase.json", + "**/.*", + "**/node_modules/**" + ], + "rewrites": [ + { + "source": "**", + "destination": "/index.html" + } + ] + } +} diff --git a/examples/stac_gallery/ios/.gitignore b/stac_playground/ios/.gitignore similarity index 100% rename from examples/stac_gallery/ios/.gitignore rename to stac_playground/ios/.gitignore diff --git a/examples/stac_gallery/ios/Flutter/AppFrameworkInfo.plist b/stac_playground/ios/Flutter/AppFrameworkInfo.plist similarity index 93% rename from examples/stac_gallery/ios/Flutter/AppFrameworkInfo.plist rename to stac_playground/ios/Flutter/AppFrameworkInfo.plist index 391a902b..1dc6cf76 100644 --- a/examples/stac_gallery/ios/Flutter/AppFrameworkInfo.plist +++ b/stac_playground/ios/Flutter/AppFrameworkInfo.plist @@ -20,5 +20,7 @@ ???? CFBundleVersion 1.0 + MinimumOSVersion + 13.0 diff --git a/examples/stac_gallery/ios/Flutter/Debug.xcconfig b/stac_playground/ios/Flutter/Debug.xcconfig similarity index 100% rename from examples/stac_gallery/ios/Flutter/Debug.xcconfig rename to stac_playground/ios/Flutter/Debug.xcconfig diff --git a/examples/stac_gallery/ios/Flutter/Release.xcconfig b/stac_playground/ios/Flutter/Release.xcconfig similarity index 100% rename from examples/stac_gallery/ios/Flutter/Release.xcconfig rename to stac_playground/ios/Flutter/Release.xcconfig diff --git a/examples/stac_gallery/ios/Podfile b/stac_playground/ios/Podfile similarity index 100% rename from examples/stac_gallery/ios/Podfile rename to stac_playground/ios/Podfile diff --git a/examples/stac_gallery/ios/Podfile.lock b/stac_playground/ios/Podfile.lock similarity index 79% rename from examples/stac_gallery/ios/Podfile.lock rename to stac_playground/ios/Podfile.lock index 4d913ee6..20045775 100644 --- a/examples/stac_gallery/ios/Podfile.lock +++ b/stac_playground/ios/Podfile.lock @@ -9,16 +9,15 @@ PODS: - sqflite_darwin (0.0.4): - Flutter - FlutterMacOS - - webview_flutter_wkwebview (0.0.1): + - url_launcher_ios (0.0.1): - Flutter - - FlutterMacOS DEPENDENCIES: - Flutter (from `Flutter`) - path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`) - shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`) - sqflite_darwin (from `.symlinks/plugins/sqflite_darwin/darwin`) - - webview_flutter_wkwebview (from `.symlinks/plugins/webview_flutter_wkwebview/darwin`) + - url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`) EXTERNAL SOURCES: Flutter: @@ -29,15 +28,15 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/shared_preferences_foundation/darwin" sqflite_darwin: :path: ".symlinks/plugins/sqflite_darwin/darwin" - webview_flutter_wkwebview: - :path: ".symlinks/plugins/webview_flutter_wkwebview/darwin" + url_launcher_ios: + :path: ".symlinks/plugins/url_launcher_ios/ios" SPEC CHECKSUMS: Flutter: cabc95a1d2626b1b06e7179b784ebcf0c0cde467 path_provider_foundation: 080d55be775b7414fd5a5ef3ac137b97b097e564 shared_preferences_foundation: 7036424c3d8ec98dfe75ff1667cb0cd531ec82bb sqflite_darwin: 20b2a3a3b70e43edae938624ce550a3cbf66a3d0 - webview_flutter_wkwebview: 1821ceac936eba6f7984d89a9f3bcb4dea99ebb2 + url_launcher_ios: 694010445543906933d732453a59da0a173ae33d PODFILE CHECKSUM: 4f1c12611da7338d21589c0b2ecd6bd20b109694 diff --git a/examples/stac_gallery/ios/Runner.xcodeproj/project.pbxproj b/stac_playground/ios/Runner.xcodeproj/project.pbxproj similarity index 89% rename from examples/stac_gallery/ios/Runner.xcodeproj/project.pbxproj rename to stac_playground/ios/Runner.xcodeproj/project.pbxproj index 1ce2cdd4..8b50d4f3 100644 --- a/examples/stac_gallery/ios/Runner.xcodeproj/project.pbxproj +++ b/stac_playground/ios/Runner.xcodeproj/project.pbxproj @@ -10,12 +10,12 @@ 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; 331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C807B294A618700263BE5 /* RunnerTests.swift */; }; 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; - 6F00BFFEE68827140D09070C /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 59A488C21334AA87EA7149F1 /* Pods_Runner.framework */; }; + 6141C3C0D3B5CB8A28A7A237 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9F61A047C117246FACC3ED6A /* Pods_Runner.framework */; }; 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; - 9638FB4F16DA8CE2830B70DF /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9AB16DEE3328F8C8AFBD298B /* Pods_RunnerTests.framework */; }; 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; + ABAF93910E40B736E943FE80 /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 284B484179A5A5B640D8E0E0 /* Pods_RunnerTests.framework */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -42,20 +42,18 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ - 05C22DDC31BD6D231294E28C /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; - 18428A0C79BCA9889271CD89 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; - 1BF9EF473160410E40B24D12 /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = ""; }; - 2A3D18938F266F193234AA82 /* Pods-RunnerTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.profile.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.profile.xcconfig"; sourceTree = ""; }; + 284B484179A5A5B640D8E0E0 /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 2E04DDAB59B0D2DB0ACB9188 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; 331C807B294A618700263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = ""; }; 331C8081294A63A400263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; - 59A488C21334AA87EA7149F1 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 4578BF2350462B75964A1069 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; - 95F5BAC4AF1ED3CF1B69B9F7 /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = ""; }; + 9116B3E77E5288D4052270BB /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = ""; }; 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -63,49 +61,52 @@ 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - 9AB16DEE3328F8C8AFBD298B /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - EB9CCD459D33F1017E4BB333 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; + 9F61A047C117246FACC3ED6A /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + A2DC1A3ED6CB156DABD6A76F /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = ""; }; + BFD0572F775F751253C8E89A /* Pods-RunnerTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.profile.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.profile.xcconfig"; sourceTree = ""; }; + C07E7695918034D239DA4053 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ - 97C146EB1CF9000F007C117D /* Frameworks */ = { + 045651CAE78D827450420D79 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 6F00BFFEE68827140D09070C /* Pods_Runner.framework in Frameworks */, + ABAF93910E40B736E943FE80 /* Pods_RunnerTests.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; - E930B7C13CC9CF828CA25766 /* Frameworks */ = { + 97C146EB1CF9000F007C117D /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 9638FB4F16DA8CE2830B70DF /* Pods_RunnerTests.framework in Frameworks */, + 6141C3C0D3B5CB8A28A7A237 /* Pods_Runner.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ - 1AC42D2B0D46D3C520716B6E /* Pods */ = { + 331C8082294A63A400263BE5 /* RunnerTests */ = { isa = PBXGroup; children = ( - 05C22DDC31BD6D231294E28C /* Pods-Runner.debug.xcconfig */, - 18428A0C79BCA9889271CD89 /* Pods-Runner.release.xcconfig */, - EB9CCD459D33F1017E4BB333 /* Pods-Runner.profile.xcconfig */, - 1BF9EF473160410E40B24D12 /* Pods-RunnerTests.debug.xcconfig */, - 95F5BAC4AF1ED3CF1B69B9F7 /* Pods-RunnerTests.release.xcconfig */, - 2A3D18938F266F193234AA82 /* Pods-RunnerTests.profile.xcconfig */, + 331C807B294A618700263BE5 /* RunnerTests.swift */, ); - path = Pods; + path = RunnerTests; sourceTree = ""; }; - 331C8082294A63A400263BE5 /* RunnerTests */ = { + 92F34A46E13ED23167446FA9 /* Pods */ = { isa = PBXGroup; children = ( - 331C807B294A618700263BE5 /* RunnerTests.swift */, - ); - path = RunnerTests; + C07E7695918034D239DA4053 /* Pods-Runner.debug.xcconfig */, + 4578BF2350462B75964A1069 /* Pods-Runner.release.xcconfig */, + 2E04DDAB59B0D2DB0ACB9188 /* Pods-Runner.profile.xcconfig */, + A2DC1A3ED6CB156DABD6A76F /* Pods-RunnerTests.debug.xcconfig */, + 9116B3E77E5288D4052270BB /* Pods-RunnerTests.release.xcconfig */, + BFD0572F775F751253C8E89A /* Pods-RunnerTests.profile.xcconfig */, + ); + name = Pods; + path = Pods; sourceTree = ""; }; 9740EEB11CF90186004384FC /* Flutter */ = { @@ -126,8 +127,8 @@ 97C146F01CF9000F007C117D /* Runner */, 97C146EF1CF9000F007C117D /* Products */, 331C8082294A63A400263BE5 /* RunnerTests */, - 1AC42D2B0D46D3C520716B6E /* Pods */, - FDB1D16E1EA1A3A793FC7332 /* Frameworks */, + 92F34A46E13ED23167446FA9 /* Pods */, + A115D802D3735616F3C1D4C5 /* Frameworks */, ); sourceTree = ""; }; @@ -155,11 +156,11 @@ path = Runner; sourceTree = ""; }; - FDB1D16E1EA1A3A793FC7332 /* Frameworks */ = { + A115D802D3735616F3C1D4C5 /* Frameworks */ = { isa = PBXGroup; children = ( - 59A488C21334AA87EA7149F1 /* Pods_Runner.framework */, - 9AB16DEE3328F8C8AFBD298B /* Pods_RunnerTests.framework */, + 9F61A047C117246FACC3ED6A /* Pods_Runner.framework */, + 284B484179A5A5B640D8E0E0 /* Pods_RunnerTests.framework */, ); name = Frameworks; sourceTree = ""; @@ -171,10 +172,10 @@ isa = PBXNativeTarget; buildConfigurationList = 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */; buildPhases = ( - F79E14CE4787F52406A5CB70 /* [CP] Check Pods Manifest.lock */, + CFAF0381FCB4241B160BDA55 /* [CP] Check Pods Manifest.lock */, 331C807D294A63A400263BE5 /* Sources */, 331C807F294A63A400263BE5 /* Resources */, - E930B7C13CC9CF828CA25766 /* Frameworks */, + 045651CAE78D827450420D79 /* Frameworks */, ); buildRules = ( ); @@ -190,14 +191,14 @@ isa = PBXNativeTarget; buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; buildPhases = ( - 83A1AC20750C3F0F0F56EB86 /* [CP] Check Pods Manifest.lock */, + C825743E93545BC2A2AE852B /* [CP] Check Pods Manifest.lock */, 9740EEB61CF901F6004384FC /* Run Script */, 97C146EA1CF9000F007C117D /* Sources */, 97C146EB1CF9000F007C117D /* Frameworks */, 97C146EC1CF9000F007C117D /* Resources */, 9705A1C41CF9048500538489 /* Embed Frameworks */, 3B06AD1E1E4923F5004D2608 /* Thin Binary */, - 9182110C26875E7D26526D19 /* [CP] Embed Pods Frameworks */, + 5B7A730ECD25262435735BF9 /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); @@ -285,61 +286,61 @@ shellPath = /bin/sh; shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; }; - 83A1AC20750C3F0F0F56EB86 /* [CP] Check Pods Manifest.lock */ = { + 5B7A730ECD25262435735BF9 /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", ); - inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", - ); - name = "[CP] Check Pods Manifest.lock"; + name = "[CP] Embed Pods Frameworks"; outputFileListPaths = ( - ); - outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; showEnvVarsInLog = 0; }; - 9182110C26875E7D26526D19 /* [CP] Embed Pods Frameworks */ = { + 9740EEB61CF901F6004384FC /* Run Script */ = { isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; buildActionMask = 2147483647; files = ( ); - inputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", + inputPaths = ( ); - name = "[CP] Embed Pods Frameworks"; - outputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", + name = "Run Script"; + outputPaths = ( ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; - showEnvVarsInLog = 0; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; }; - 9740EEB61CF901F6004384FC /* Run Script */ = { + C825743E93545BC2A2AE852B /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; - alwaysOutOfDate = 1; buildActionMask = 2147483647; files = ( ); + inputFileListPaths = ( + ); inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( ); - name = "Run Script"; outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; }; - F79E14CE4787F52406A5CB70 /* [CP] Check Pods Manifest.lock */ = { + CFAF0381FCB4241B160BDA55 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -415,6 +416,7 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; CLANG_ANALYZER_NONNULL = YES; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; @@ -444,6 +446,7 @@ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_NO_COMMON_BLOCKS = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; @@ -468,13 +471,14 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + DEVELOPMENT_TEAM = W2B7PMH9SQ; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", ); - PRODUCT_BUNDLE_IDENTIFIER = com.stac.stacGallery; + PRODUCT_BUNDLE_IDENTIFIER = com.stac.stacPlayground; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_VERSION = 5.0; @@ -484,14 +488,14 @@ }; 331C8088294A63A400263BE5 /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 1BF9EF473160410E40B24D12 /* Pods-RunnerTests.debug.xcconfig */; + baseConfigurationReference = A2DC1A3ED6CB156DABD6A76F /* Pods-RunnerTests.debug.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; GENERATE_INFOPLIST_FILE = YES; MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = com.stac.stacGallery.RunnerTests; + PRODUCT_BUNDLE_IDENTIFIER = com.stac.stacPlayground.RunnerTests; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; @@ -502,14 +506,14 @@ }; 331C8089294A63A400263BE5 /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 95F5BAC4AF1ED3CF1B69B9F7 /* Pods-RunnerTests.release.xcconfig */; + baseConfigurationReference = 9116B3E77E5288D4052270BB /* Pods-RunnerTests.release.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; GENERATE_INFOPLIST_FILE = YES; MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = com.stac.stacGallery.RunnerTests; + PRODUCT_BUNDLE_IDENTIFIER = com.stac.stacPlayground.RunnerTests; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_VERSION = 5.0; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner"; @@ -518,14 +522,14 @@ }; 331C808A294A63A400263BE5 /* Profile */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 2A3D18938F266F193234AA82 /* Pods-RunnerTests.profile.xcconfig */; + baseConfigurationReference = BFD0572F775F751253C8E89A /* Pods-RunnerTests.profile.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; GENERATE_INFOPLIST_FILE = YES; MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = com.stac.stacGallery.RunnerTests; + PRODUCT_BUNDLE_IDENTIFIER = com.stac.stacPlayground.RunnerTests; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_VERSION = 5.0; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner"; @@ -536,6 +540,7 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; CLANG_ANALYZER_NONNULL = YES; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; @@ -565,6 +570,7 @@ DEBUG_INFORMATION_FORMAT = dwarf; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_DYNAMIC_NO_PIC = NO; GCC_NO_COMMON_BLOCKS = YES; @@ -591,6 +597,7 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; CLANG_ANALYZER_NONNULL = YES; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; @@ -620,6 +627,7 @@ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_NO_COMMON_BLOCKS = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; @@ -646,14 +654,14 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; - DEVELOPMENT_TEAM = TTQFBUD682; + DEVELOPMENT_TEAM = W2B7PMH9SQ; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", ); - PRODUCT_BUNDLE_IDENTIFIER = com.stac.stacGallery; + PRODUCT_BUNDLE_IDENTIFIER = com.stac.stacPlayground; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; @@ -669,13 +677,14 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + DEVELOPMENT_TEAM = W2B7PMH9SQ; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", ); - PRODUCT_BUNDLE_IDENTIFIER = com.stac.stacGallery; + PRODUCT_BUNDLE_IDENTIFIER = com.stac.stacPlayground; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_VERSION = 5.0; diff --git a/examples/stac_gallery/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/stac_playground/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata similarity index 100% rename from examples/stac_gallery/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata rename to stac_playground/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata diff --git a/examples/stac_gallery/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/stac_playground/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist similarity index 100% rename from examples/stac_gallery/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist rename to stac_playground/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist diff --git a/examples/stac_gallery/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/stac_playground/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings similarity index 100% rename from examples/stac_gallery/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings rename to stac_playground/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings diff --git a/examples/stac_gallery/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/stac_playground/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme similarity index 100% rename from examples/stac_gallery/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme rename to stac_playground/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme diff --git a/examples/stac_gallery/ios/Runner.xcworkspace/contents.xcworkspacedata b/stac_playground/ios/Runner.xcworkspace/contents.xcworkspacedata similarity index 100% rename from examples/stac_gallery/ios/Runner.xcworkspace/contents.xcworkspacedata rename to stac_playground/ios/Runner.xcworkspace/contents.xcworkspacedata diff --git a/examples/stac_gallery/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/stac_playground/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist similarity index 100% rename from examples/stac_gallery/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist rename to stac_playground/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist diff --git a/examples/stac_gallery/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/stac_playground/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings similarity index 100% rename from examples/stac_gallery/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings rename to stac_playground/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings diff --git a/stac_playground/ios/Runner/AppDelegate.swift b/stac_playground/ios/Runner/AppDelegate.swift new file mode 100644 index 00000000..62666446 --- /dev/null +++ b/stac_playground/ios/Runner/AppDelegate.swift @@ -0,0 +1,13 @@ +import Flutter +import UIKit + +@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/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/stac_playground/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json similarity index 100% rename from examples/stac_gallery/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json rename to stac_playground/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json diff --git a/examples/stac_gallery/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png b/stac_playground/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png similarity index 100% rename from examples/stac_gallery/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png rename to stac_playground/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png diff --git a/examples/stac_gallery/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png b/stac_playground/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png similarity index 100% rename from examples/stac_gallery/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png rename to stac_playground/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png diff --git a/examples/stac_gallery/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png b/stac_playground/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png similarity index 100% rename from examples/stac_gallery/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png rename to stac_playground/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png diff --git a/examples/stac_gallery/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png b/stac_playground/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png similarity index 100% rename from examples/stac_gallery/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png rename to stac_playground/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png diff --git a/examples/stac_gallery/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png b/stac_playground/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png similarity index 100% rename from examples/stac_gallery/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png rename to stac_playground/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png diff --git a/examples/stac_gallery/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png b/stac_playground/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png similarity index 100% rename from examples/stac_gallery/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png rename to stac_playground/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png diff --git a/examples/stac_gallery/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png b/stac_playground/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png similarity index 100% rename from examples/stac_gallery/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png rename to stac_playground/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png diff --git a/examples/stac_gallery/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png b/stac_playground/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png similarity index 100% rename from examples/stac_gallery/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png rename to stac_playground/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png diff --git a/examples/stac_gallery/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png b/stac_playground/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png similarity index 100% rename from examples/stac_gallery/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png rename to stac_playground/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png diff --git a/examples/stac_gallery/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png b/stac_playground/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png similarity index 100% rename from examples/stac_gallery/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png rename to stac_playground/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png diff --git a/examples/stac_gallery/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png b/stac_playground/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png similarity index 100% rename from examples/stac_gallery/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png rename to stac_playground/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png diff --git a/examples/stac_gallery/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png b/stac_playground/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png similarity index 100% rename from examples/stac_gallery/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png rename to stac_playground/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png diff --git a/examples/stac_gallery/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png b/stac_playground/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png similarity index 100% rename from examples/stac_gallery/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png rename to stac_playground/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png diff --git a/examples/stac_gallery/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png b/stac_playground/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png similarity index 100% rename from examples/stac_gallery/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png rename to stac_playground/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png diff --git a/examples/stac_gallery/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png b/stac_playground/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png similarity index 100% rename from examples/stac_gallery/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png rename to stac_playground/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png diff --git a/examples/stac_gallery/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json b/stac_playground/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json similarity index 100% rename from examples/stac_gallery/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json rename to stac_playground/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json diff --git a/examples/stac_gallery/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png b/stac_playground/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png similarity index 100% rename from examples/stac_gallery/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png rename to stac_playground/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png diff --git a/examples/stac_gallery/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png b/stac_playground/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png similarity index 100% rename from examples/stac_gallery/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png rename to stac_playground/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png diff --git a/examples/stac_gallery/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png b/stac_playground/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png similarity index 100% rename from examples/stac_gallery/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png rename to stac_playground/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png diff --git a/examples/stac_gallery/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md b/stac_playground/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md similarity index 100% rename from examples/stac_gallery/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md rename to stac_playground/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md diff --git a/examples/stac_gallery/ios/Runner/Base.lproj/LaunchScreen.storyboard b/stac_playground/ios/Runner/Base.lproj/LaunchScreen.storyboard similarity index 100% rename from examples/stac_gallery/ios/Runner/Base.lproj/LaunchScreen.storyboard rename to stac_playground/ios/Runner/Base.lproj/LaunchScreen.storyboard diff --git a/examples/stac_gallery/ios/Runner/Base.lproj/Main.storyboard b/stac_playground/ios/Runner/Base.lproj/Main.storyboard similarity index 100% rename from examples/stac_gallery/ios/Runner/Base.lproj/Main.storyboard rename to stac_playground/ios/Runner/Base.lproj/Main.storyboard diff --git a/examples/stac_gallery/ios/Runner/Info.plist b/stac_playground/ios/Runner/Info.plist similarity index 95% rename from examples/stac_gallery/ios/Runner/Info.plist rename to stac_playground/ios/Runner/Info.plist index dab2951b..4df824b8 100644 --- a/examples/stac_gallery/ios/Runner/Info.plist +++ b/stac_playground/ios/Runner/Info.plist @@ -5,7 +5,7 @@ CFBundleDevelopmentRegion $(DEVELOPMENT_LANGUAGE) CFBundleDisplayName - Stac Gallery + Stac Playground CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier @@ -13,7 +13,7 @@ CFBundleInfoDictionaryVersion 6.0 CFBundleName - stac_gallery + stac_playground CFBundlePackageType APPL CFBundleShortVersionString diff --git a/examples/stac_gallery/ios/Runner/Runner-Bridging-Header.h b/stac_playground/ios/Runner/Runner-Bridging-Header.h similarity index 100% rename from examples/stac_gallery/ios/Runner/Runner-Bridging-Header.h rename to stac_playground/ios/Runner/Runner-Bridging-Header.h diff --git a/stac_playground/ios/RunnerTests/RunnerTests.swift b/stac_playground/ios/RunnerTests/RunnerTests.swift new file mode 100644 index 00000000..86a7c3b1 --- /dev/null +++ b/stac_playground/ios/RunnerTests/RunnerTests.swift @@ -0,0 +1,12 @@ +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/stac_playground/lib/app/cubit/home_cubit.dart b/stac_playground/lib/app/cubit/home_cubit.dart new file mode 100644 index 00000000..4def3a8e --- /dev/null +++ b/stac_playground/lib/app/cubit/home_cubit.dart @@ -0,0 +1,731 @@ +import 'dart:convert'; + +import 'package:flutter/services.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:stac_playground/app/cubit/home_state.dart'; +import 'package:stac_playground/data/component_entries.dart'; +import 'package:stac_playground/data/playground_entry.dart'; + +/// Superset of playground screens and gallery components. +final List playgroundEntries = [ + const PlaygroundEntry( + id: 'hello_stac', + title: 'Hello Stac', + description: 'Welcome screen introducing the Stac SDUI framework', + json: helloStacSample, + dartCode: helloStacDartCode, + icon: 'waving_hand', + ), + const PlaygroundEntry( + id: 'form_screen', + title: 'Form Screen', + description: 'Sign-in form with validation, fields and actions', + json: formSample, + dartCode: formDartCode, + icon: 'login', + ), + ...componentEntries, +]; + +/// Rebuilds [json] as a plain `Map` tree. +/// +/// The inline samples ([helloStacSample], [formSample]) are `const` map +/// literals, so on the web (and VM) their nested maps are typed +/// `` — Stac's generated `fromJson` does +/// `json['child'] as Map`, which rejects them and surfaces as +/// a "Stac Parse Error" (most visibly on buttons). Round-tripping through JSON +/// yields real `Map`/`List` nodes throughout. +Map asRenderableJson(Map json) => + jsonDecode(jsonEncode(json)) as Map; + +class HomeCubit extends Cubit { + HomeCubit() + : super( + HomeState( + jsonData: asRenderableJson(helloStacSample), + selectedEntry: playgroundEntries.first, + dartCode: helloStacDartCode, + showCodeView: true, + ), + ); + + /// Loads an entry's JSON and Dart sources (inline or from assets) and + /// makes it the selected entry. + Future selectEntry(PlaygroundEntry entry) async { + var json = entry.json; + var dart = entry.dartCode; + try { + json ??= jsonDecode(await rootBundle.loadString(entry.jsonAsset!)) + as Map; + dart ??= await rootBundle.loadString(entry.dartAsset!); + } catch (_) { + return; + } + emit( + state.copyWith( + selectedEntry: entry, + jsonData: asRenderableJson(json), + dartCode: dart, + edited: false, + ), + ); + } + + void setQuery(String query) { + emit(state.copyWith(query: query)); + } + + /// Replace the rendered JSON with the latest valid editor content. + void updateJsonData(Map json) { + emit(state.copyWith(jsonData: json)); + } + + void setEdited(bool edited) { + if (state.edited != edited) { + emit(state.copyWith(edited: edited)); + } + } + + void toggleDarkMode() { + emit(state.copyWith(darkMode: !state.darkMode)); + } + + void toggleCodeView() { + emit(state.copyWith(showCodeView: !state.showCodeView)); + } + + void reduceScale() { + emit(state.copyWith(scale: (state.scale - 0.1).clamp(0.3, 3.0))); + } + + void increaseScale() { + emit(state.copyWith(scale: (state.scale + 0.1).clamp(0.3, 3.0))); + } + + void resetScale() { + emit(state.copyWith(scale: 1.0)); + } + + void setCodeLanguage(CodeLanguage language) { + emit(state.copyWith(codeLanguage: language)); + } + + void setView(PlaygroundView view) { + emit(state.copyWith(view: view)); + } + + void setMobileDark(bool dark) { + emit(state.copyWith(mobileDark: dark)); + } +} + +const Map formSample = { + "type": "scaffold", + "backgroundColor": "#F4F6FA", + "appBar": {"type": "appBar"}, + "body": { + "type": "form", + "child": { + "type": "padding", + "padding": { + "left": 24, + "right": 24, + }, + "child": { + "type": "column", + "crossAxisAlignment": "start", + "children": [ + { + "type": "text", + "data": "BettrDo Sign in", + "style": { + "fontSize": 24, + "fontWeight": "w900", + "height": 1.3, + } + }, + { + "type": "sizedBox", + "height": 24, + }, + { + "type": "textFormField", + "id": "email", + "autovalidateMode": "onUserInteraction", + "validatorRules": [ + { + "rule": "isEmail", + "message": "Please enter a valid email", + } + ], + "style": { + "fontSize": 16, + "fontWeight": "w400", + "height": 1.5, + }, + "decoration": { + "hintText": "Email", + "filled": true, + "fillColor": "#FFFFFF", + "border": { + "type": "outlineInputBorder", + "borderRadius": 8, + "color": "#24151D29", + } + }, + }, + { + "type": "sizedBox", + "height": 16, + }, + { + "type": "textFormField", + "autovalidateMode": "onUserInteraction", + "validatorRules": [ + { + "rule": "isPassword", + "message": "Please enter a valid password", + } + ], + "obscureText": true, + "maxLines": 1, + "style": { + "fontSize": 16, + "fontWeight": "w400", + "height": 1.5, + }, + "decoration": { + "hintText": "Password", + "filled": true, + "fillColor": "#FFFFFF", + "border": { + "type": "outlineInputBorder", + "borderRadius": 8, + "color": "#24151D29", + } + }, + }, + { + "type": "sizedBox", + "height": 32, + }, + { + "type": "filledButton", + "style": { + "backgroundColor": "#151D29", + "shape": { + "borderRadius": 8, + } + }, + "onPressed": {}, + "child": { + "type": "padding", + "padding": { + "top": 14, + "bottom": 14, + "left": 16, + "right": 16, + }, + "child": { + "type": "row", + "mainAxisAlignment": "spaceBetween", + "children": [ + { + "type": "text", + "data": "Proceed", + }, + { + "type": "icon", + "iconType": "material", + "icon": "arrow_forward", + } + ], + }, + } + }, + { + "type": "sizedBox", + "height": 16, + }, + { + "type": "align", + "alignment": "center", + "child": { + "type": "textButton", + "onPressed": {}, + "child": { + "type": "text", + "data": "Forgot password?", + "style": { + "fontSize": 15, + "fontWeight": "w500", + "color": "#4745B4", + } + } + } + }, + { + "type": "sizedBox", + "height": 8, + }, + { + "type": "align", + "alignment": "center", + "child": { + "type": "text", + "data": "Don't have an account? ", + "style": { + "fontSize": 15, + "fontWeight": "w400", + "color": "#000000", + }, + "children": [ + { + "data": "Sign Up for BettrDo", + "style": { + "fontSize": 15, + "fontWeight": "w500", + "color": "#4745B4", + } + } + ], + }, + } + ], + }, + } + } +}; + +const Map helloStacSample = { + "type": "scaffold", + "body": { + "type": "padding", + "padding": {"top": 80, "left": 24, "right": 24, "bottom": 24}, + "child": { + "type": "column", + "crossAxisAlignment": "start", + "children": [ + { + "type": "container", + "width": 56, + "height": 56, + "decoration": { + "borderRadius": 12, + }, + "clipBehavior": "hardEdge", + "child": { + "type": "image", + "src": + "https://pbs.twimg.com/profile_images/1886322776921042944/5Nveo4M2_400x400.png" + } + }, + { + "type": "sizedBox", + "height": 40, + }, + { + "type": "image", + "src": + "https://raw.githubusercontent.com/StacDev/stac/refs/heads/dev/assets/Welcome%20to.png", + }, + { + "type": "text", + "data": "Stac Playground", + "style": { + "fontSize": 36, + "fontWeight": "w600", + "height": 1.3, + } + }, + { + "type": "sizedBox", + "height": 32, + }, + { + "type": "text", + "data": + "Stac is a Server-Driven UI (SDUI) framework for Flutter. Stac allows you to build beautiful cross-platform applications with JSON in real time.", + "style": { + "fontSize": 18, + "fontWeight": "w400", + "height": 1.5, + } + }, + {"type": "spacer"}, + { + "type": "container", + "height": 1, + "widht": 1000, + "color": "#20010810", + }, + { + "type": "sizedBox", + "height": 24, + }, + { + "type": "text", + "data": "Follow us for more updates:", + "style": { + "fontSize": 18, + "fontWeight": "w400", + "height": 1.5, + "color": "#80010810" + } + }, + { + "type": "sizedBox", + "height": 20, + }, + { + "type": "column", + "spacing": 20, + "children": [ + { + "type": "row", + "spacing": 20, + "children": [ + { + "type": "container", + "width": 44, + "height": 44, + "decoration": { + "borderRadius": 12, + }, + "clipBehavior": "hardEdge", + "child": { + "type": "image", + "src": + "https://raw.githubusercontent.com/StacDev/stac/refs/heads/dev/assets/github.png" + } + }, + { + "type": "text", + "data": "github.com/StacDev", + "style": { + "fontSize": 18, + "fontWeight": "w500", + "height": 1.5, + } + } + ], + }, + { + "type": "row", + "spacing": 20, + "children": [ + { + "type": "container", + "width": 44, + "height": 44, + "decoration": { + "borderRadius": 12, + }, + "clipBehavior": "hardEdge", + "child": { + "type": "image", + "src": + "https://raw.githubusercontent.com/StacDev/stac/refs/heads/dev/assets/x.png" + } + }, + { + "type": "text", + "data": "x.com/stac_dev", + "style": { + "fontSize": 18, + "fontWeight": "w500", + "height": 1.5, + } + } + ], + }, + { + "type": "row", + "spacing": 20, + "children": [ + { + "type": "container", + "width": 44, + "height": 44, + "decoration": { + "borderRadius": 12, + }, + "clipBehavior": "hardEdge", + "child": { + "type": "image", + "src": + "https://raw.githubusercontent.com/StacDev/stac/refs/heads/dev/assets/linkedin.png" + } + }, + { + "type": "text", + "data": "/company/StacDev", + "style": { + "fontSize": 18, + "fontWeight": "w500", + "height": 1.5, + } + } + ], + } + ], + } + ], + } + } +}; + +/// Stac DSL source for the `hello_stac` screen. `stac build` compiles this +/// to the JSON in [helloStacSample]. +const String helloStacDartCode = r''' +import 'package:stac_core/stac_core.dart'; + +@StacScreen(screenName: 'hello_stac') +StacWidget helloStac() { + return StacScaffold( + body: StacPadding( + padding: StacEdgeInsets.only(top: 80, left: 24, right: 24, bottom: 24), + child: StacColumn( + crossAxisAlignment: StacCrossAxisAlignment.start, + children: [ + StacContainer( + width: 56, + height: 56, + decoration: StacBoxDecoration( + borderRadius: StacBorderRadius.all(12), + ), + clipBehavior: StacClip.hardEdge, + child: StacImage( + src: + 'https://pbs.twimg.com/profile_images/1886322776921042944/5Nveo4M2_400x400.png', + ), + ), + StacSizedBox(height: 40), + StacImage( + src: + 'https://raw.githubusercontent.com/StacDev/stac/refs/heads/dev/assets/Welcome%20to.png', + ), + StacText( + data: 'Stac Playground', + style: StacTextStyle( + fontSize: 36, + fontWeight: StacFontWeight.w600, + height: 1.3, + ), + ), + StacSizedBox(height: 32), + StacText( + data: + 'Stac is a Server-Driven UI (SDUI) framework for Flutter. Stac allows you to build beautiful cross-platform applications with JSON in real time.', + style: StacTextStyle( + fontSize: 18, + fontWeight: StacFontWeight.w400, + height: 1.5, + ), + ), + StacSpacer(), + StacContainer(height: 1, width: 1000, color: '#20010810'), + StacSizedBox(height: 24), + StacText( + data: 'Follow us for more updates:', + style: StacTextStyle( + fontSize: 18, + fontWeight: StacFontWeight.w400, + height: 1.5, + color: '#80010810', + ), + ), + StacSizedBox(height: 20), + StacColumn( + spacing: 20, + children: [ + _socialRow( + icon: + 'https://raw.githubusercontent.com/StacDev/stac/refs/heads/dev/assets/github.png', + handle: 'github.com/StacDev', + ), + _socialRow( + icon: + 'https://raw.githubusercontent.com/StacDev/stac/refs/heads/dev/assets/x.png', + handle: 'x.com/stac_dev', + ), + _socialRow( + icon: + 'https://raw.githubusercontent.com/StacDev/stac/refs/heads/dev/assets/linkedin.png', + handle: '/company/StacDev', + ), + ], + ), + ], + ), + ), + ); +} + +/// The DSL is plain Dart, so repeated UI can be extracted into helpers. +StacWidget _socialRow({required String icon, required String handle}) { + return StacRow( + spacing: 20, + children: [ + StacContainer( + width: 44, + height: 44, + decoration: StacBoxDecoration( + borderRadius: StacBorderRadius.all(12), + ), + clipBehavior: StacClip.hardEdge, + child: StacImage(src: icon), + ), + StacText( + data: handle, + style: StacTextStyle( + fontSize: 18, + fontWeight: StacFontWeight.w500, + height: 1.5, + ), + ), + ], + ); +} +'''; + +/// Stac DSL source for the `form_screen` screen. `stac build` compiles this +/// to the JSON in [formSample]. +const String formDartCode = r''' +import 'package:stac_core/stac_core.dart'; + +@StacScreen(screenName: 'form_screen') +StacWidget formScreen() { + return StacScaffold( + backgroundColor: '#F4F6FA', + appBar: StacAppBar(), + body: StacForm( + child: StacPadding( + padding: StacEdgeInsets.only(left: 24, right: 24), + child: StacColumn( + crossAxisAlignment: StacCrossAxisAlignment.start, + children: [ + StacText( + data: 'BettrDo Sign in', + style: StacTextStyle( + fontSize: 24, + fontWeight: StacFontWeight.w900, + height: 1.3, + ), + ), + StacSizedBox(height: 24), + StacTextFormField( + id: 'email', + autovalidateMode: StacAutovalidateMode.onUserInteraction, + validatorRules: [ + StacFormFieldValidator( + rule: 'isEmail', + message: 'Please enter a valid email', + ), + ], + style: StacTextStyle( + fontSize: 16, + fontWeight: StacFontWeight.w400, + height: 1.5, + ), + decoration: _fieldDecoration(hintText: 'Email'), + ), + StacSizedBox(height: 16), + StacTextFormField( + autovalidateMode: StacAutovalidateMode.onUserInteraction, + validatorRules: [ + StacFormFieldValidator( + rule: 'isPassword', + message: 'Please enter a valid password', + ), + ], + obscureText: true, + maxLines: 1, + style: StacTextStyle( + fontSize: 16, + fontWeight: StacFontWeight.w400, + height: 1.5, + ), + decoration: _fieldDecoration(hintText: 'Password'), + ), + StacSizedBox(height: 32), + StacFilledButton( + style: StacButtonStyle( + backgroundColor: '#151D29', + shape: StacRoundedRectangleBorder( + borderRadius: StacBorderRadius.all(8), + ), + ), + child: StacPadding( + padding: StacEdgeInsets.only( + top: 14, + bottom: 14, + left: 16, + right: 16, + ), + child: StacRow( + mainAxisAlignment: StacMainAxisAlignment.spaceBetween, + children: [ + StacText(data: 'Proceed'), + StacIcon(icon: 'arrow_forward'), + ], + ), + ), + ), + StacSizedBox(height: 16), + StacAlign( + alignment: StacAlignmentDirectional.center, + child: StacTextButton( + child: StacText( + data: 'Forgot password?', + style: StacTextStyle( + fontSize: 15, + fontWeight: StacFontWeight.w500, + color: '#4745B4', + ), + ), + ), + ), + StacSizedBox(height: 8), + StacAlign( + alignment: StacAlignmentDirectional.center, + child: StacText( + data: "Don't have an account? ", + style: StacTextStyle( + fontSize: 15, + fontWeight: StacFontWeight.w400, + color: '#000000', + ), + children: [ + StacTextSpan( + text: 'Sign Up for BettrDo', + style: StacTextStyle( + fontSize: 15, + fontWeight: StacFontWeight.w500, + color: '#4745B4', + ), + ), + ], + ), + ), + ], + ), + ), + ), + ); +} + +/// Shared outline decoration for the sign-in fields. +StacInputDecoration _fieldDecoration({required String hintText}) { + return StacInputDecoration( + hintText: hintText, + filled: true, + fillColor: '#FFFFFF', + border: StacInputBorder( + type: StacInputBorderType.outlineInputBorder, + borderRadius: StacBorderRadius.all(8), + color: '#24151D29', + ), + ); +} +'''; diff --git a/stac_playground/lib/app/cubit/home_state.dart b/stac_playground/lib/app/cubit/home_state.dart new file mode 100644 index 00000000..609f2291 --- /dev/null +++ b/stac_playground/lib/app/cubit/home_state.dart @@ -0,0 +1,74 @@ +import 'package:stac_playground/data/playground_entry.dart'; + +/// The language shown in the code editor. Dart is the Stac DSL source that +/// `stac build` compiles to the deployed JSON. +enum CodeLanguage { dart, json } + +/// Top-level playground layout: editor + live preview, or Dart and JSON +/// editors side by side with no preview. +enum PlaygroundView { preview, codeDiff } + +class HomeState { + HomeState({ + required this.jsonData, + required this.selectedEntry, + this.dartCode = '', + this.showCodeView = true, + this.scale = 1.0, + this.darkMode = false, + this.edited = false, + this.codeLanguage = CodeLanguage.dart, + this.view = PlaygroundView.preview, + this.query = '', + this.mobileDark = true, + }); + + /// The widget tree currently rendered by the preview. + final Map jsonData; + final PlaygroundEntry selectedEntry; + + /// Dart DSL source for [selectedEntry] (inline or loaded from assets). + final String dartCode; + final bool showCodeView; + final double scale; + final bool darkMode; + final CodeLanguage codeLanguage; + final PlaygroundView view; + + /// Search query filtering the entry index. + final String query; + + /// Whether the mobile UI renders in dark theme. + final bool mobileDark; + + /// Whether the JSON has been modified since the entry was loaded. + final bool edited; + + HomeState copyWith({ + Map? jsonData, + PlaygroundEntry? selectedEntry, + String? dartCode, + bool? showCodeView, + double? scale, + bool? darkMode, + bool? edited, + CodeLanguage? codeLanguage, + PlaygroundView? view, + String? query, + bool? mobileDark, + }) { + return HomeState( + jsonData: jsonData ?? this.jsonData, + selectedEntry: selectedEntry ?? this.selectedEntry, + dartCode: dartCode ?? this.dartCode, + showCodeView: showCodeView ?? this.showCodeView, + scale: scale ?? this.scale, + darkMode: darkMode ?? this.darkMode, + edited: edited ?? this.edited, + codeLanguage: codeLanguage ?? this.codeLanguage, + view: view ?? this.view, + query: query ?? this.query, + mobileDark: mobileDark ?? this.mobileDark, + ); + } +} diff --git a/stac_playground/lib/app/embed/embed_message_channel.dart b/stac_playground/lib/app/embed/embed_message_channel.dart new file mode 100644 index 00000000..382c7954 --- /dev/null +++ b/stac_playground/lib/app/embed/embed_message_channel.dart @@ -0,0 +1,122 @@ +import 'dart:convert'; +import 'dart:developer' as developer; +import 'dart:js_interop'; + +import 'package:web/web.dart' as web; + +typedef EmbedJsonPayloadHandler = void Function( + Map jsonPayload); + +class EmbedMessageChannel { + static const _previewMessageType = 'stac-preview-json'; + static const _logName = 'embed-channel'; + + JSFunction? _messageListener; + + void start(EmbedJsonPayloadHandler onPayload) { + dispose(); + _messageListener = ((web.Event event) { + _debugLog('message event received'); + + final payload = _parseMessageEvent(event); + if (payload == null) { + _debugLog('payload rejected by parser/validator'); + return; + } + + _debugLog('payload accepted (type=${payload['type']})'); + onPayload(payload); + }).toJS; + web.window.addEventListener('message', _messageListener); + _debugLog('message listener attached'); + } + + void dispose() { + if (_messageListener != null) { + web.window.removeEventListener('message', _messageListener); + _messageListener = null; + } + } + + Map? _parseMessageEvent(web.Event event) { + try { + final messageEvent = event as web.MessageEvent; + return _extractPayload(_jsAnyToDart(messageEvent.data)); + } catch (error, stackTrace) { + _debugLog('event decode failed', error: error, stackTrace: stackTrace); + return null; + } + } + + Map? _extractPayload(Object? data) { + final normalizedData = _normalizeToDart(data); + if (normalizedData is! Map) return null; + + if (normalizedData['type'] == _previewMessageType) { + final payload = _normalizeToDart(normalizedData['payload']); + if (payload is! Map) return null; + return _isStacWidgetJson(payload) ? payload : null; + } + + return _isStacWidgetJson(normalizedData) ? normalizedData : null; + } + + Object? _normalizeToDart(Object? value) { + if (value == null) return null; + + if (value is String) { + try { + return jsonDecode(value); + } on FormatException { + return value; + } + } + + if (value is num || value is bool) { + return value; + } + + if (value is Map) { + return _toStringKeyMap(value); + } + + if (value is List) { + return value.map(_normalizeToDart).toList(); + } + + return value; + } + + Map _toStringKeyMap(Map source) { + final result = {}; + source.forEach((key, value) { + result[key.toString()] = _normalizeToDart(value); + }); + return result; + } + + Object? _jsAnyToDart(JSAny? value) { + if (value == null) return null; + return value.dartify(); + } + + bool _isStacWidgetJson(Map json) { + return json['type'] is String; + } + + void _debugLog( + String message, { + Object? error, + StackTrace? stackTrace, + }) { + assert(() { + developer.log( + message, + name: _logName, + error: error, + stackTrace: stackTrace, + ); + return true; + }()); + } +} diff --git a/stac_playground/lib/app/embed/embed_screen.dart b/stac_playground/lib/app/embed/embed_screen.dart new file mode 100644 index 00000000..e9f3c6ed --- /dev/null +++ b/stac_playground/lib/app/embed/embed_screen.dart @@ -0,0 +1,78 @@ +import 'package:flutter/gestures.dart'; +import 'package:flutter/material.dart'; +import 'package:stac/stac.dart'; +import 'package:stac_playground/app/embed/embed_message_channel.dart'; + +class EmbedScreen extends StatefulWidget { + const EmbedScreen({ + super.key, + this.initialJson, + }); + + final Map? initialJson; + + @override + State createState() => _EmbedScreenState(); +} + +class _EmbedScreenState extends State { + final EmbedMessageChannel _embedMessageChannel = EmbedMessageChannel(); + late Map _jsonData; + + final Map embedHelloStacSample = { + "type": "scaffold", + "body": { + "type": "center", + "child": { + "type": "text", + "data": "Hello Stac", + } + } + }; + + @override + void initState() { + super.initState(); + _jsonData = widget.initialJson ?? embedHelloStacSample; + + _embedMessageChannel.start((jsonPayload) { + if (!mounted) return; + setState(() { + _jsonData = jsonPayload; + }); + }); + } + + @override + void dispose() { + _embedMessageChannel.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return MaterialApp( + scrollBehavior: const _EmbedScrollBehavior(), + debugShowCheckedModeBanner: false, + theme: ThemeData.dark(), + home: Scaffold( + body: Center( + child: _jsonData.isEmpty + ? const SizedBox.shrink() + : Stac.fromJson(_jsonData, context), + ), + ), + ); + } +} + +class _EmbedScrollBehavior extends MaterialScrollBehavior { + const _EmbedScrollBehavior(); + + @override + Set get dragDevices => { + PointerDeviceKind.touch, + PointerDeviceKind.mouse, + PointerDeviceKind.trackpad, + }; +} diff --git a/stac_playground/lib/app/embed/embed_screen_stub.dart b/stac_playground/lib/app/embed/embed_screen_stub.dart new file mode 100644 index 00000000..934137d8 --- /dev/null +++ b/stac_playground/lib/app/embed/embed_screen_stub.dart @@ -0,0 +1,14 @@ +import 'package:flutter/material.dart'; + +/// Non-web stand-in for the web-only embed screen, which relies on +/// `dart:js_interop` and is compiled only for web builds. +class EmbedScreen extends StatelessWidget { + const EmbedScreen({super.key}); + + @override + Widget build(BuildContext context) { + return const Scaffold( + body: Center(child: Text('Embedding is available on web only.')), + ); + } +} diff --git a/stac_playground/lib/app/home_screen.dart b/stac_playground/lib/app/home_screen.dart new file mode 100644 index 00000000..6cb083c1 --- /dev/null +++ b/stac_playground/lib/app/home_screen.dart @@ -0,0 +1,67 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:stac_playground/app/cubit/home_cubit.dart'; +import 'package:stac_playground/app/cubit/home_state.dart'; +import 'package:stac_playground/app/widgets/code_preview.dart'; +import 'package:stac_playground/app/widgets/console_icon_rail.dart'; +import 'package:stac_playground/app/widgets/editor_panel.dart'; +import 'package:stac_playground/app/mobile/mobile_shell.dart'; +import 'package:stac_playground/app/widgets/index_panel.dart'; +import 'package:stac_playground/theme/app_theme.dart'; + +class HomeScreen extends StatelessWidget { + const HomeScreen({super.key}); + + @override + Widget build(BuildContext context) { + // Narrow viewports (and mobile builds) get the Console mobile UI; the + // desktop web console renders above this breakpoint. + if (MediaQuery.sizeOf(context).width < 700) { + return const MobileShell(); + } + return Scaffold( + backgroundColor: context.colors.background, + body: BlocBuilder( + buildWhen: (previous, current) => + previous.showCodeView != current.showCodeView || + previous.view != current.view, + builder: (context, state) { + if (state.view == PlaygroundView.codeDiff) { + return Row( + children: [ + const ConsoleIconRail(), + const IndexPanel(), + const Expanded( + child: EditorPanel(languageOverride: CodeLanguage.dart), + ), + Container(width: 1, color: context.colors.outline), + const Expanded( + child: EditorPanel(languageOverride: CodeLanguage.json), + ), + ], + ); + } + return LayoutBuilder( + builder: (context, constraints) { + // 560 like the console design, shrinking on narrow windows so + // the editor keeps a usable width. + final previewWidth = + (constraints.maxWidth * 0.36).clamp(400.0, 560.0); + return Row( + children: [ + const ConsoleIconRail(), + const IndexPanel(), + if (state.showCodeView) ...[ + const Expanded(child: EditorPanel()), + SizedBox(width: previewWidth, child: const CodePreview()), + ] else + const Expanded(child: CodePreview()), + ], + ); + }, + ); + }, + ), + ); + } +} diff --git a/stac_playground/lib/app/mobile/mobile_shell.dart b/stac_playground/lib/app/mobile/mobile_shell.dart new file mode 100644 index 00000000..a3b4d2bf --- /dev/null +++ b/stac_playground/lib/app/mobile/mobile_shell.dart @@ -0,0 +1,994 @@ +import 'dart:convert'; +import 'dart:ui' show ImageFilter; + +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:google_fonts/google_fonts.dart'; +import 'package:phosphoricons_flutter/phosphoricons_flutter.dart'; +import 'package:re_editor/re_editor.dart'; +import 'package:re_highlight/languages/dart.dart'; +import 'package:re_highlight/languages/json.dart'; +import 'package:stac/stac.dart'; +import 'package:stac_playground/app/cubit/home_cubit.dart'; +import 'package:stac_playground/app/cubit/home_state.dart'; +import 'package:stac_playground/app/widgets/code_preview.dart'; +import 'package:stac_playground/data/playground_entry.dart'; +import 'package:url_launcher/url_launcher.dart'; + +/// Console mobile design tokens, mirrored for dark and light themes. +class MobileColors { + const MobileColors({ + required this.surface, + required this.surfaceBright, + required this.container, + required this.outline, + required this.outlineVariant, + required this.onSurface, + required this.onSurfaceVariant, + required this.onSurfaceVariantII, + required this.secondary, + required this.iconHex, + }); + + /// Values come from the Console Figma file's theme variables + /// (Surface, On Surface, Outline, Secondary, ...) for each mode. + factory MobileColors.of(bool dark) => dark + ? const MobileColors( + surface: Color(0xFF0B0B0D), + surfaceBright: Color(0xFF101112), + container: Color(0x0AFFFFFF), + outline: Color(0x0FFFFFFF), + outlineVariant: Color(0x1AFFFFFF), + onSurface: Colors.white, + onSurfaceVariant: Color(0xB2FFFFFF), + onSurfaceVariantII: Color(0x80FFFFFF), + secondary: Color(0xFF50D59D), + iconHex: '#B2FFFFFF', + ) + : const MobileColors( + surface: Color(0xFFF3F3F3), + surfaceBright: Colors.white, + container: Color(0x0A07090A), + outline: Color(0x1407090A), + outlineVariant: Color(0x1F07090A), + onSurface: Color(0xFF07090A), + onSurfaceVariant: Color(0xB207090A), + onSurfaceVariantII: Color(0x8007090A), + secondary: Color(0xFF15803D), + iconHex: '#B207090A', + ); + + final Color surface; + final Color surfaceBright; + final Color container; + final Color outline; + final Color outlineVariant; + final Color onSurface; + final Color onSurfaceVariant; + final Color onSurfaceVariantII; + + /// Accent green (Console `Secondary` token, darker in light mode). + final Color secondary; + + /// [onSurfaceVariant] as a hex string for Stac-rendered icons. + final String iconHex; +} + +/// Root of the mobile experience: the explore list. +class MobileShell extends StatelessWidget { + const MobileShell({super.key}); + + @override + Widget build(BuildContext context) { + return const MobileExploreScreen(); + } +} + +class MobileExploreScreen extends StatelessWidget { + const MobileExploreScreen({super.key}); + + @override + Widget build(BuildContext context) { + return BlocBuilder( + buildWhen: (p, c) => p.mobileDark != c.mobileDark || p.query != c.query, + builder: (context, state) { + final colors = MobileColors.of(state.mobileDark); + final query = state.query.toLowerCase(); + final entries = playgroundEntries + .where((e) => + query.isEmpty || + e.id.contains(query) || + e.title.toLowerCase().contains(query)) + .toList(); + return Scaffold( + backgroundColor: colors.surface, + body: SafeArea( + child: Column( + children: [ + _ExploreTopBar(colors: colors), + Expanded( + child: ListView( + padding: const EdgeInsets.fromLTRB(16, 24, 16, 40), + children: [ + // Hero: Title Large, medium weight, 1.3 line height. + Text( + 'Explore Screens,', + style: TextStyle( + fontSize: 23, + fontWeight: FontWeight.w500, + height: 1.3, + fontVariations: const [FontVariation('wght', 500)], + color: colors.onSurface, + ), + ), + Text( + 'Components, Code, etc', + style: TextStyle( + fontSize: 23, + fontWeight: FontWeight.w500, + height: 1.3, + fontVariations: const [FontVariation('wght', 500)], + color: colors.onSurfaceVariant, + ), + ), + const SizedBox(height: 24), + _MobileSearchField(colors: colors), + const SizedBox(height: 24), + Text( + query.isEmpty + ? '${entries.length} COMPONENTS' + : '${entries.length} ' + 'RESULT${entries.length == 1 ? '' : 'S'}', + style: TextStyle( + fontSize: 13, + fontWeight: FontWeight.w500, + height: 1.3, + letterSpacing: 1.04, + fontVariations: const [FontVariation('wght', 500)], + color: colors.onSurfaceVariantII, + ), + ), + const SizedBox(height: 10), + for (final entry in entries) ...[ + _EntryCard( + entry: entry, + colors: colors, + onTap: () { + final cubit = context.read(); + cubit.selectEntry(entry); + Navigator.of(context).push( + MaterialPageRoute( + builder: (_) => BlocProvider.value( + value: cubit, + child: const MobileDetailScreen(), + ), + ), + ); + }, + ), + const SizedBox(height: 10), + ], + ], + ), + ), + ], + ), + ), + ); + }, + ); + } +} + +/// 56px top bar: circular logo, "Stac Playground" wordmark, menu icon. +class _ExploreTopBar extends StatelessWidget { + const _ExploreTopBar({required this.colors}); + + final MobileColors colors; + + @override + Widget build(BuildContext context) { + return SizedBox( + height: 56, + child: Row( + children: [ + const SizedBox(width: 16), + Container( + width: 28, + height: 28, + decoration: BoxDecoration( + shape: BoxShape.circle, + border: Border.all(color: colors.outline), + ), + child: ClipOval( + child: Image.asset( + 'assets/images/logo_console.png', + fit: BoxFit.cover, + ), + ), + ), + const SizedBox(width: 8), + Text( + 'Stac', + style: TextStyle( + fontSize: 18, + fontWeight: FontWeight.w500, + height: 1.3, + fontVariations: const [FontVariation('wght', 500)], + color: colors.onSurface, + ), + ), + const SizedBox(width: 7), + Text( + 'Playground', + style: TextStyle( + fontSize: 16, + height: 1.5, + color: colors.secondary, + ), + ), + const Spacer(), + InkWell( + onTap: () => _openMenu(context), + child: PhosphorIcon( + PhosphorIcons.list, + size: 20, + color: colors.onSurface, + ), + ), + const SizedBox(width: 18), + ], + ), + ); + } +} + +/// 48px search input: container fill, hairline border, 8px radius. +class _MobileSearchField extends StatelessWidget { + const _MobileSearchField({required this.colors}); + + final MobileColors colors; + + @override + Widget build(BuildContext context) { + return Container( + height: 48, + padding: const EdgeInsets.symmetric(horizontal: 16), + decoration: BoxDecoration( + color: colors.container, + borderRadius: BorderRadius.circular(8), + border: Border.all(color: colors.outlineVariant, width: 0.5), + ), + child: Row( + children: [ + PhosphorIcon( + PhosphorIcons.magnifyingGlass, + size: 20, + color: colors.onSurfaceVariantII, + ), + const SizedBox(width: 12), + Expanded( + child: TextField( + onChanged: (v) => context.read().setQuery(v), + style: TextStyle( + fontSize: 16, + height: 1.5, + color: colors.onSurface, + ), + decoration: InputDecoration( + isCollapsed: true, + border: InputBorder.none, + hintText: 'Search..', + hintStyle: TextStyle( + fontSize: 16, + height: 1.5, + color: colors.onSurfaceVariantII, + ), + ), + ), + ), + ], + ), + ); + } +} + +/// Component card: 8px radius container, circular icon chip, title and +/// description per the Console mobile design. +class _EntryCard extends StatelessWidget { + const _EntryCard({ + required this.entry, + required this.colors, + required this.onTap, + }); + + final PlaygroundEntry entry; + final MobileColors colors; + final VoidCallback onTap; + + @override + Widget build(BuildContext context) { + return InkWell( + onTap: onTap, + borderRadius: BorderRadius.circular(8), + child: Container( + constraints: const BoxConstraints(minHeight: 94), + padding: const EdgeInsets.fromLTRB(16, 12, 16, 12), + decoration: BoxDecoration( + color: colors.container, + borderRadius: BorderRadius.circular(8), + border: Border.all(color: colors.outlineVariant, width: 0.5), + ), + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + padding: const EdgeInsets.all(8), + decoration: BoxDecoration( + color: colors.container, + shape: BoxShape.circle, + ), + child: entry.icon == null + ? PhosphorIcon( + PhosphorIcons.square, + size: 16, + color: colors.onSurfaceVariant, + ) + : SizedBox( + width: 16, + height: 16, + child: Stac.fromJson( + { + 'type': 'icon', + 'iconType': entry.iconType, + 'icon': entry.icon, + 'size': 16, + 'color': colors.iconHex, + }, + context, + ), + ), + ), + const SizedBox(width: 12), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + entry.title, + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.w600, + height: 1.5, + fontVariations: const [FontVariation('wght', 600)], + color: colors.onSurface, + ), + ), + Text( + entry.description.isEmpty + ? 'Stac ${entry.id} example' + : entry.description, + style: TextStyle( + fontSize: 14, + height: 1.5, + color: colors.onSurfaceVariant, + ), + ), + ], + ), + ), + ], + ), + ), + ); + } +} + +/// Opens the full-screen menu overlay, per the Console mobile design. +void _openMenu(BuildContext context) { + final cubit = context.read(); + showGeneralDialog( + context: context, + barrierDismissible: false, + barrierColor: Colors.transparent, + transitionDuration: const Duration(milliseconds: 150), + transitionBuilder: (_, animation, __, child) => + FadeTransition(opacity: animation, child: child), + pageBuilder: (_, __, ___) => BlocProvider.value( + value: cubit, + child: const _MobileMenuOverlay(), + ), + ); +} + +/// Blurred surface overlay with plain link rows and the theme row. +class _MobileMenuOverlay extends StatelessWidget { + const _MobileMenuOverlay(); + + @override + Widget build(BuildContext context) { + return BlocBuilder( + buildWhen: (p, c) => p.mobileDark != c.mobileDark, + builder: (context, state) { + final colors = MobileColors.of(state.mobileDark); + Widget row({required String label, required VoidCallback onTap}) => + InkWell( + onTap: onTap, + child: SizedBox( + width: double.infinity, + child: Text( + label, + style: TextStyle( + fontSize: 16, + height: 1.5, + color: colors.onSurface, + ), + ), + ), + ); + final divider = Container(height: 1, color: colors.outline); + return Material( + color: Colors.transparent, + child: BackdropFilter( + filter: ImageFilter.blur(sigmaX: 12, sigmaY: 12), + child: Container( + color: colors.surface.withValues(alpha: 0.95), + child: SafeArea( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SizedBox( + height: 56, + child: Row( + children: [ + const SizedBox(width: 16), + Container( + width: 28, + height: 28, + decoration: BoxDecoration( + shape: BoxShape.circle, + border: Border.all(color: colors.outline), + ), + child: ClipOval( + child: Image.asset( + 'assets/images/logo_console.png', + fit: BoxFit.cover, + ), + ), + ), + const SizedBox(width: 8), + Text( + 'Stac', + style: TextStyle( + fontSize: 18, + fontWeight: FontWeight.w500, + height: 1.3, + fontVariations: const [ + FontVariation('wght', 500), + ], + color: colors.onSurface, + ), + ), + const SizedBox(width: 7), + Text( + 'Playground', + style: TextStyle( + fontSize: 16, + height: 1.5, + color: colors.secondary, + ), + ), + const Spacer(), + InkWell( + onTap: () => Navigator.of(context).pop(), + child: SizedBox( + width: 36, + height: 36, + child: Center( + child: PhosphorIcon( + PhosphorIcons.x, + size: 24, + color: colors.onSurface, + ), + ), + ), + ), + const SizedBox(width: 12), + ], + ), + ), + Padding( + padding: const EdgeInsets.symmetric( + horizontal: 56, + vertical: 32, + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + row( + label: 'Documentation', + onTap: () => + launchUrl(Uri.parse('https://docs.stac.dev')), + ), + const SizedBox(height: 16), + divider, + const SizedBox(height: 16), + row( + label: 'Github', + onTap: () => launchUrl( + Uri.parse('https://github.com/StacDev/stac'), + ), + ), + const SizedBox(height: 16), + divider, + const SizedBox(height: 16), + row( + label: 'LinkedIn', + onTap: () => launchUrl( + Uri.parse( + 'https://www.linkedin.com/company/stacdev', + ), + ), + ), + const SizedBox(height: 16), + divider, + const SizedBox(height: 16), + row( + label: 'X', + onTap: () => launchUrl( + Uri.parse('https://x.com/stac_dev'), + ), + ), + const SizedBox(height: 16), + divider, + const SizedBox(height: 16), + InkWell( + onTap: () => context + .read() + .setMobileDark(!state.mobileDark), + child: Row( + children: [ + Expanded( + child: Text( + 'Theme', + style: TextStyle( + fontSize: 16, + height: 1.5, + color: colors.onSurface, + ), + ), + ), + Text( + state.mobileDark ? 'Dark' : 'Light', + style: TextStyle( + fontSize: 16, + height: 1.5, + color: colors.onSurface, + ), + ), + const SizedBox(width: 6), + PhosphorIcon( + state.mobileDark + ? PhosphorIcons.moonStars + : PhosphorIcons.sunDim, + size: 20, + color: colors.onSurface, + ), + ], + ), + ), + ], + ), + ), + ], + ), + ), + ), + ), + ); + }, + ); + } +} + +/// Component detail: Preview / Dart / JSON tabs, per the Console mobile design. +class MobileDetailScreen extends StatefulWidget { + const MobileDetailScreen({super.key}); + + @override + State createState() => _MobileDetailScreenState(); +} + +class _MobileDetailScreenState extends State { + int _tab = 0; + + @override + Widget build(BuildContext context) { + return BlocBuilder( + builder: (context, state) { + final colors = MobileColors.of(state.mobileDark); + return Scaffold( + backgroundColor: colors.surface, + body: SafeArea( + child: Column( + children: [ + // Header on surface-bright with hairline bottom border. + Container( + height: 56, + decoration: BoxDecoration( + color: colors.surfaceBright, + border: Border(bottom: BorderSide(color: colors.outline)), + ), + child: Row( + children: [ + const SizedBox(width: 8), + InkWell( + onTap: () => Navigator.of(context).pop(), + child: Padding( + padding: const EdgeInsets.all(6), + child: PhosphorIcon( + PhosphorIcons.caretLeft, + size: 20, + color: colors.onSurface, + ), + ), + ), + const SizedBox(width: 4), + Expanded( + child: Text( + state.selectedEntry.title, + overflow: TextOverflow.ellipsis, + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.w500, + height: 1.3, + fontVariations: const [FontVariation('wght', 500)], + color: colors.onSurface, + ), + ), + ), + // Toggles the component preview theme only; the app + // theme is switched from the explore drawer. + _HeaderIcon( + icon: state.darkMode + ? PhosphorIcons.sunDim + : PhosphorIcons.moonStars, + colors: colors, + onTap: () => context.read().toggleDarkMode(), + ), + const SizedBox(width: 4), + _HeaderIcon( + icon: PhosphorIcons.frameCorners, + colors: colors, + onTap: () => Navigator.of(context).push( + MaterialPageRoute( + builder: (_) => BlocProvider.value( + value: context.read(), + child: const _FullScreenPreview(), + ), + ), + ), + ), + const SizedBox(width: 12), + ], + ), + ), + // Centered tab bar with the accent underline on the active tab. + Container( + height: 48, + decoration: BoxDecoration( + color: colors.surfaceBright, + border: Border(bottom: BorderSide(color: colors.outline)), + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + _MobileTab( + icon: PhosphorIcons.crop, + label: 'Preview', + active: _tab == 0, + colors: colors, + onTap: () => setState(() => _tab = 0), + ), + const SizedBox(width: 64), + _MobileTab( + customIcon: Opacity( + opacity: _tab == 1 ? 1 : 0.7, + child: Image.asset( + 'assets/images/dart_logo.png', + width: 16, + height: 16, + fit: BoxFit.contain, + ), + ), + label: 'Dart', + active: _tab == 1, + colors: colors, + onTap: () => setState(() => _tab = 1), + ), + const SizedBox(width: 64), + _MobileTab( + icon: PhosphorIcons.fileCode, + label: 'JSON', + active: _tab == 2, + colors: colors, + onTap: () => setState(() => _tab = 2), + ), + ], + ), + ), + Expanded(child: _tabBody(state)), + ], + ), + ), + ); + }, + ); + } + + Widget _tabBody(HomeState state) { + switch (_tab) { + case 1: + return _MobileCodeView( + key: ValueKey('dart-${state.selectedEntry.id}-${state.mobileDark}'), + text: state.dartCode, + isDart: true, + dark: state.mobileDark, + ); + case 2: + return _MobileCodeView( + key: ValueKey('json-${state.selectedEntry.id}-${state.mobileDark}'), + text: const JsonEncoder.withIndent(' ').convert(state.jsonData), + isDart: false, + dark: state.mobileDark, + ); + default: + return _MobilePreview(state: state); + } + } +} + +/// 36px tappable header icon with 24px glyph. +class _HeaderIcon extends StatelessWidget { + const _HeaderIcon({ + required this.icon, + required this.colors, + required this.onTap, + }); + + final IconData icon; + final MobileColors colors; + final VoidCallback onTap; + + @override + Widget build(BuildContext context) { + return InkWell( + onTap: onTap, + borderRadius: BorderRadius.circular(6), + child: SizedBox( + width: 36, + height: 36, + child: Center( + child: PhosphorIcon(icon, size: 24, color: colors.onSurfaceVariant), + ), + ), + ); + } +} + +class _MobileTab extends StatelessWidget { + const _MobileTab({ + this.icon, + this.customIcon, + required this.label, + required this.active, + required this.colors, + required this.onTap, + }); + + final IconData? icon; + final Widget? customIcon; + final String label; + final bool active; + final MobileColors colors; + final VoidCallback onTap; + + @override + Widget build(BuildContext context) { + final color = active ? colors.onSurface : colors.onSurfaceVariant; + return InkWell( + onTap: onTap, + child: Container( + height: 48, + decoration: BoxDecoration( + border: Border( + bottom: BorderSide( + color: active ? colors.secondary : Colors.transparent, + ), + ), + ), + child: Row( + children: [ + customIcon ?? PhosphorIcon(icon!, size: 16, color: color), + const SizedBox(width: 4), + Text( + label, + style: TextStyle(fontSize: 14, height: 1.5, color: color), + ), + ], + ), + ), + ); + } +} + +class _MobilePreview extends StatelessWidget { + const _MobilePreview({required this.state}); + + final HomeState state; + + @override + Widget build(BuildContext context) { + final jsonData = state.jsonData; + return MaterialApp( + debugShowCheckedModeBanner: false, + scrollBehavior: const AppScrollBehavior(), + // Follows the preview theme toggle in the detail header, independent + // of the app theme. + theme: state.darkMode ? ThemeData.dark() : ThemeData.light(), + home: Stac.fromJson(jsonData, context), + ); + } +} + +class _FullScreenPreview extends StatelessWidget { + const _FullScreenPreview(); + + @override + Widget build(BuildContext context) { + return BlocBuilder( + builder: (context, state) { + final colors = MobileColors.of(state.mobileDark); + return Scaffold( + backgroundColor: colors.surface, + body: Stack( + children: [ + Positioned.fill(child: _MobilePreview(state: state)), + Positioned( + top: MediaQuery.of(context).padding.top + 8, + right: 16, + child: InkWell( + onTap: () => Navigator.of(context).pop(), + child: Container( + padding: const EdgeInsets.all(8), + decoration: BoxDecoration( + color: colors.surfaceBright.withValues(alpha: 0.9), + shape: BoxShape.circle, + border: Border.all(color: colors.outlineVariant), + ), + child: PhosphorIcon( + PhosphorIcons.x, + size: 16, + color: colors.onSurface, + ), + ), + ), + ), + ], + ), + ); + }, + ); + } +} + +/// Read-only code view with the console editor styling, in a dark +/// (VS Code Dark+) or light (VS Code Light+) variant following the app theme. +class _MobileCodeView extends StatefulWidget { + const _MobileCodeView({ + super.key, + required this.text, + required this.isDart, + required this.dark, + }); + + final String text; + final bool isDart; + final bool dark; + + @override + State<_MobileCodeView> createState() => _MobileCodeViewState(); +} + +class _MobileCodeViewState extends State<_MobileCodeView> { + late final _font = GoogleFonts.jetBrainsMono( + fontSize: 12, + color: widget.dark ? Colors.white : const Color(0xFF1F1F1F), + height: 1.5, + ); + late final CodeLineEditingController _controller = + CodeLineEditingController.fromText(widget.text); + + Map _theme() { + final f = _font; + if (widget.dark) { + return { + 'root': f.copyWith(color: const Color(0xFFD4D4D4)), + 'punctuation': f.copyWith(color: const Color(0xFFD7BA7D)), + 'comment': f.copyWith(color: const Color(0xFF6A9955)), + 'keyword': f.copyWith(color: const Color(0xFF569CD6)), + 'literal': f.copyWith(color: const Color(0xFF569CD6)), + 'string': f.copyWith(color: const Color(0xFFCE9178)), + 'number': f.copyWith(color: const Color(0xFFB5CEA8)), + 'attr': f.copyWith(color: const Color(0xFF9CDCFE)), + 'meta': f.copyWith(color: const Color(0xFF9CDCFE)), + 'title': f.copyWith(color: const Color(0xFFDCDCAA)), + 'title.class': f.copyWith(color: const Color(0xFF4EC9B0)), + 'built_in': f.copyWith(color: const Color(0xFF4EC9B0)), + }; + } + return { + 'root': f.copyWith(color: const Color(0xFF1F1F1F)), + 'punctuation': f.copyWith(color: const Color(0xFF3B3B3B)), + 'comment': f.copyWith(color: const Color(0xFF008000)), + 'keyword': f.copyWith(color: const Color(0xFF0000FF)), + 'literal': f.copyWith(color: const Color(0xFF0000FF)), + 'string': f.copyWith(color: const Color(0xFFA31515)), + 'number': f.copyWith(color: const Color(0xFF098658)), + 'attr': f.copyWith(color: const Color(0xFF0451A5)), + 'meta': f.copyWith(color: const Color(0xFF0451A5)), + 'title': f.copyWith(color: const Color(0xFF795E26)), + 'title.class': f.copyWith(color: const Color(0xFF267F99)), + 'built_in': f.copyWith(color: const Color(0xFF267F99)), + }; + } + + @override + void dispose() { + _controller.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + final lineNumber = widget.dark + ? Colors.white.withValues(alpha: 0.4) + : Colors.black.withValues(alpha: 0.35); + return Container( + color: widget.dark ? const Color(0xFF101112) : const Color(0xFFF7F7F8), + child: CodeEditor( + controller: _controller, + readOnly: true, + style: CodeEditorStyle( + fontFamily: 'JetBrainsMono', + fontSize: 12, + fontHeight: 1.5, + textColor: + widget.dark ? const Color(0xFFD4D4D4) : const Color(0xFF1F1F1F), + codeTheme: CodeHighlightTheme( + languages: widget.isDart + ? {'dart': CodeHighlightThemeMode(mode: langDart)} + : {'json': CodeHighlightThemeMode(mode: langJson)}, + theme: _theme(), + ), + ), + wordWrap: false, + indicatorBuilder: + (context, editingController, chunkController, notifier) { + return Row( + children: [ + const SizedBox(width: 8), + DefaultCodeLineNumber( + controller: editingController, + notifier: notifier, + textStyle: _font.copyWith(color: lineNumber), + focusedTextStyle: _font, + ), + const SizedBox(width: 8), + ], + ); + }, + ), + ); + } +} diff --git a/stac_playground/lib/app/widgets/code_preview.dart b/stac_playground/lib/app/widgets/code_preview.dart new file mode 100644 index 00000000..4ad88305 --- /dev/null +++ b/stac_playground/lib/app/widgets/code_preview.dart @@ -0,0 +1,292 @@ +import 'package:flutter/gestures.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:phosphoricons_flutter/phosphoricons_flutter.dart'; +import 'package:stac/stac.dart'; +import 'package:stac_playground/app/cubit/home_cubit.dart'; +import 'package:stac_playground/app/cubit/home_state.dart'; +import 'package:stac_playground/app/widgets/section_nav.dart'; +import 'package:stac_playground/theme/app_theme.dart'; + +/// The right-hand preview pane: theme/device/zoom controls on top of a +/// live device frame rendering the current Stac JSON. +class CodePreview extends StatelessWidget { + const CodePreview({super.key}); + + @override + Widget build(BuildContext context) { + return BlocBuilder( + builder: (context, state) { + final jsonData = state.jsonData; + return Container( + decoration: BoxDecoration( + color: context.colors.background, + border: Border(left: BorderSide(color: context.colors.outline)), + ), + child: Column( + children: [ + _PreviewNav(state: state), + Expanded( + child: jsonData.isEmpty + ? const _EmptyPreview() + : _DeviceFrame(state: state, jsonData: jsonData), + ), + ], + ), + ); + }, + ); + } +} + +class _PreviewNav extends StatelessWidget { + const _PreviewNav({required this.state}); + + final HomeState state; + + @override + Widget build(BuildContext context) { + return SectionNav( + child: Row( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + const SizedBox(width: 12), + Center( + child: Tooltip( + message: state.showCodeView ? 'Hide editor' : 'Show editor', + child: InkWell( + onTap: () => context.read().toggleCodeView(), + hoverColor: context.colors.surfaceVariant, + child: PhosphorIcon( + state.showCodeView + ? PhosphorIcons.caretLineLeft + : PhosphorIcons.caretLineRight, + size: 18, + color: context.colors.onBackground2, + ), + ), + ), + ), + const SizedBox(width: 12), + const Center(child: NavDivider()), + const SizedBox(width: 12), + Center(child: _ThemeSelector(darkMode: state.darkMode)), + const Spacer(), + Center( + child: _NavIconButton( + icon: PhosphorIcons.magnifyingGlassPlus, + tooltip: 'Zoom in', + onTap: () => context.read().increaseScale(), + ), + ), + const SizedBox(width: 12), + Center( + child: _NavIconButton( + icon: PhosphorIcons.magnifyingGlassMinus, + tooltip: 'Zoom out (${(state.scale * 100).toInt()}%)', + onTap: () => context.read().reduceScale(), + ), + ), + const SizedBox(width: 12), + ], + ), + ); + } +} + +class _ThemeSelector extends StatelessWidget { + const _ThemeSelector({required this.darkMode}); + + final bool darkMode; + + @override + Widget build(BuildContext context) { + return MenuAnchor( + menuChildren: [ + for (final dark in [true, false]) + MenuItemButton( + onPressed: () { + if (darkMode != dark) { + context.read().toggleDarkMode(); + } + }, + child: Text( + dark ? 'Dark Theme' : 'Light Theme', + style: TextStyle( + fontSize: 12, + height: 1.5, + color: context.colors.onBackground, + ), + ), + ), + ], + builder: (context, controller, child) => InkWell( + onTap: () => controller.isOpen ? controller.close() : controller.open(), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'THEME', + style: TextStyle( + fontSize: 9, + fontWeight: FontWeight.w500, + height: 1.3, + letterSpacing: 0.72, + fontVariations: const [FontVariation('wght', 500)], + color: Colors.white.withValues(alpha: 0.5), + ), + ), + Row( + children: [ + Text( + darkMode ? 'Dark Theme' : 'Light Theme', + style: TextStyle( + fontSize: 12, + height: 1.5, + color: context.colors.onBackground, + ), + ), + const SizedBox(width: 4), + PhosphorIcon( + PhosphorIcons.caretDown, + size: 10, + color: context.colors.onBackground, + ), + ], + ), + ], + ), + ), + ); + } +} + +class _NavIconButton extends StatelessWidget { + const _NavIconButton({ + required this.icon, + required this.tooltip, + required this.onTap, + }); + + final IconData icon; + final String tooltip; + final VoidCallback onTap; + + @override + Widget build(BuildContext context) { + return Tooltip( + message: tooltip, + child: InkWell( + onTap: onTap, + hoverColor: context.colors.surfaceVariant, + child: PhosphorIcon( + icon, + size: 18, + color: context.colors.onBackground2, + ), + ), + ); + } +} + +class _DeviceFrame extends StatelessWidget { + const _DeviceFrame({required this.state, required this.jsonData}); + + final HomeState state; + final Map jsonData; + + @override + Widget build(BuildContext context) { + const frameSize = Size(390, 844); + const radius = 40.0; + + // Built outside the scroll/fit wrappers so the nested MaterialApp is + // never (re)inflated during layout. + final frame = Container( + width: frameSize.width, + height: frameSize.height, + clipBehavior: Clip.hardEdge, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(radius), + border: Border.all(color: Colors.white.withValues(alpha: 0.15)), + boxShadow: [ + BoxShadow( + color: Colors.white.withValues(alpha: 0.04), + blurRadius: 40, + ), + ], + ), + child: ClipRRect( + borderRadius: BorderRadius.circular(radius), + child: MaterialApp( + debugShowCheckedModeBanner: false, + scrollBehavior: const AppScrollBehavior(), + theme: state.darkMode ? ThemeData.dark() : ThemeData.light(), + home: Stac.fromJson(jsonData, context), + ), + ), + ); + + return ScrollConfiguration( + behavior: const AppScrollBehavior(), + child: SingleChildScrollView( + child: Center( + child: SingleChildScrollView( + scrollDirection: Axis.horizontal, + child: Padding( + padding: const EdgeInsets.all(24), + child: SizedBox.fromSize( + size: frameSize * state.scale, + child: FittedBox( + fit: BoxFit.contain, + child: frame, + ), + ), + ), + ), + ), + ), + ); + } +} + +class _EmptyPreview extends StatelessWidget { + const _EmptyPreview(); + + @override + Widget build(BuildContext context) { + return Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + PhosphorIcon( + PhosphorIcons.swatchesThin, + size: 96, + color: context.colors.onBackground.withValues(alpha: 0.24), + ), + const SizedBox(height: 16), + Text( + 'Select a sample or write a code\nto start preview', + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 18, + fontWeight: FontWeight.w500, + height: 1.5, + color: context.colors.onBackground.withValues(alpha: 0.24), + ), + ) + ], + ); + } +} + +class AppScrollBehavior extends MaterialScrollBehavior { + const AppScrollBehavior(); + + @override + Set get dragDevices => { + PointerDeviceKind.touch, + PointerDeviceKind.mouse, + PointerDeviceKind.trackpad, + }; +} diff --git a/stac_playground/lib/app/widgets/console_icon_rail.dart b/stac_playground/lib/app/widgets/console_icon_rail.dart new file mode 100644 index 00000000..50c4b8a2 --- /dev/null +++ b/stac_playground/lib/app/widgets/console_icon_rail.dart @@ -0,0 +1,107 @@ +import 'package:flutter/material.dart'; +import 'package:phosphoricons_flutter/phosphoricons_flutter.dart'; +import 'package:stac_playground/theme/app_theme.dart'; +import 'package:url_launcher/url_launcher.dart'; + +/// The 48px icon rail on the far left: logo on top, utility and social +/// links pinned to the bottom. +class ConsoleIconRail extends StatelessWidget { + const ConsoleIconRail({super.key}); + + @override + Widget build(BuildContext context) { + return Container( + width: 48, + decoration: BoxDecoration( + color: const Color(0x80101112), + border: Border(right: BorderSide(color: context.colors.outline)), + ), + child: Column( + spacing: 8, + children: [ + Padding( + padding: const EdgeInsets.all(12), + child: ClipRRect( + borderRadius: BorderRadius.circular(6), + child: Image.asset( + 'assets/images/logo_console.png', + height: 24, + width: 24, + fit: BoxFit.cover, + ), + ), + ), + const Spacer(), + _RailIcon( + icon: PhosphorIcons.fileText, + tooltip: 'Documentation', + onTap: () => launchUrl(Uri.parse('https://docs.stac.dev')), + ), + SizedBox( + height: 13, + child: Center( + child: Container( + width: 8, + height: 1, + color: Colors.white.withValues(alpha: 0.15), + ), + ), + ), + _RailIcon( + icon: PhosphorIcons.githubLogo, + tooltip: 'GitHub', + onTap: () => + launchUrl(Uri.parse('https://github.com/StacDev/stac')), + ), + _RailIcon( + icon: PhosphorIcons.linkedinLogo, + tooltip: 'LinkedIn', + onTap: () => launchUrl( + Uri.parse('https://www.linkedin.com/company/stacdev'), + ), + ), + _RailIcon( + icon: PhosphorIcons.xLogo, + tooltip: 'X', + onTap: () => launchUrl(Uri.parse('https://x.com/stac_dev')), + ), + const SizedBox(height: 3), + ], + ), + ); + } +} + +class _RailIcon extends StatelessWidget { + const _RailIcon({ + required this.icon, + required this.tooltip, + required this.onTap, + }); + + final IconData icon; + final String tooltip; + final VoidCallback onTap; + + @override + Widget build(BuildContext context) { + return Tooltip( + message: tooltip, + child: InkWell( + onTap: onTap, + hoverColor: context.colors.surfaceVariant, + child: SizedBox( + width: 48, + height: 30, + child: Center( + child: PhosphorIcon( + icon, + size: 18, + color: context.colors.onSurfaceVariant, + ), + ), + ), + ), + ); + } +} diff --git a/stac_playground/lib/app/widgets/editor_panel.dart b/stac_playground/lib/app/widgets/editor_panel.dart new file mode 100644 index 00000000..8cca6054 --- /dev/null +++ b/stac_playground/lib/app/widgets/editor_panel.dart @@ -0,0 +1,580 @@ +import 'dart:async'; +import 'dart:convert'; + +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:google_fonts/google_fonts.dart'; +import 'package:phosphoricons_flutter/phosphoricons_flutter.dart'; +import 'package:re_editor/re_editor.dart'; +import 'package:re_highlight/languages/dart.dart'; +import 'package:re_highlight/languages/json.dart'; +import 'package:re_highlight/re_highlight.dart'; +import 'package:stac_playground/app/cubit/home_cubit.dart'; +import 'package:stac_playground/app/cubit/home_state.dart'; +import 'package:stac_playground/app/widgets/find_panel.dart'; +import 'package:stac_playground/app/widgets/section_nav.dart'; +import 'package:stac_playground/data/dsl_to_json.dart'; +import 'package:stac_playground/theme/app_theme.dart'; + +/// re_highlight's Dart grammar is purely regex-based, so it only tags a few +/// things (class definitions, annotations, strings, numbers, keywords) and +/// leaves most identifiers as plain foreground text — which reads as washed +/// out next to VS Code. VS Code colors the rest with the analysis server's +/// semantic tokens, which we can't run here; instead we approximate them from +/// Dart's rigid naming conventions with a handful of extra match rules, +/// injected at the front of the grammar so they win over plain-text fallback. +/// Patched once, before the grammar is compiled on first use. +final Mode _dartHighlightMode = _patchDartMode(); + +// Lowercase words that must stay keyword-blue even when followed by `(`, so the +// call-expression rule below doesn't repaint control flow as a function name. +const String _dartKeywordGuard = + r'(?!(?:if|for|while|switch|return|new|await|yield|assert|is|as|in|else|do' + r'|try|catch|finally|throw|rethrow|break|continue|case|default|void|true' + r'|false|null|var|final|const|late|required|super|this|typedef|extends' + r'|implements|with|mixin|enum|class|import|export|part|library|show|hide' + r'|get|set|factory|operator|async|sync)\b)'; + +Mode _patchDartMode() { + langDart.contains?.insertAll(0, [ + // Named-argument labels — `fontSize:`, `child:` → parameter light-blue. + Mode(className: 'property', begin: r'\b[a-z_][A-Za-z0-9_]*(?=\s*:)'), + // Call expressions — `helloStac(`, `.only(`, `.all(` → function yellow. + Mode( + className: 'title.function', + begin: '\\b$_dartKeywordGuard[a-z_][A-Za-z0-9_]*(?=\\s*\\()', + ), + // Member access after a dot — `.w600`, `.start`, `.maxFinite` → light-blue. + Mode(className: 'property', begin: r'(?<=\.)[a-z_][A-Za-z0-9_]*'), + // Type / constructor usages (UpperCamelCase) — `StacText` → class teal. + Mode(className: 'title.class', begin: r'\b[A-Z][A-Za-z0-9_]*'), + ]); + return langDart; +} + +/// Minimal context menu for the code editor, mirroring the Stac Console. +class _EditorContextMenuController implements SelectionToolbarController { + const _EditorContextMenuController(); + + @override + void hide(BuildContext context) {} + + @override + void show({ + required BuildContext context, + required CodeLineEditingController controller, + required TextSelectionToolbarAnchors anchors, + Rect? renderRect, + required LayerLink layerLink, + required ValueNotifier visibility, + }) { + showMenu( + context: context, + position: RelativeRect.fromSize( + anchors.primaryAnchor & const Size(150, double.infinity), + MediaQuery.sizeOf(context), + ), + items: [ + PopupMenuItem( + child: const Text('Cut'), + onTap: () => controller.cut(), + ), + PopupMenuItem( + child: const Text('Copy'), + onTap: () => controller.copy(), + ), + PopupMenuItem( + child: const Text('Paste'), + onTap: () => controller.paste(), + ), + ], + ); + } +} + +/// The central editor pane: filename bar on top of the code editor. +/// +/// With [languageOverride] set the pane is pinned to that language and the +/// Dart/JSON switcher is hidden — used by the side-by-side code diff view. +class EditorPanel extends StatelessWidget { + const EditorPanel({super.key, this.languageOverride}); + + final CodeLanguage? languageOverride; + + @override + Widget build(BuildContext context) { + return Container( + color: context.colors.surface, + child: BlocBuilder( + buildWhen: (previous, current) => + previous.selectedEntry.id != current.selectedEntry.id || + previous.dartCode != current.dartCode || + (languageOverride == null && + previous.codeLanguage != current.codeLanguage), + builder: (context, state) { + final language = languageOverride ?? state.codeLanguage; + return _CodeEditorContent( + key: ValueKey( + '${state.selectedEntry.id}-$language-${languageOverride != null}', + ), + entryId: state.selectedEntry.id, + language: language, + showLanguageToggle: languageOverride == null, + ); + }, + ), + ); + } +} + +class _CodeEditorContent extends StatefulWidget { + const _CodeEditorContent({ + super.key, + required this.entryId, + required this.language, + this.showLanguageToggle = true, + }); + + final String entryId; + final CodeLanguage language; + final bool showLanguageToggle; + + @override + State<_CodeEditorContent> createState() => _CodeEditorContentState(); +} + +class _CodeEditorContentState extends State<_CodeEditorContent> { + final _defaultFont = GoogleFonts.jetBrainsMono( + fontSize: 13, + color: Colors.white, + height: 1.5, + ); + + late final String _baselineText; + late final bool _baselineIsPristine; + late final CodeLineEditingController _controller; + late final CodeFindController _findController; + String _lastText = ''; + + /// Set when the Dart source falls outside the parseable subset, so the + /// preview is showing the last good tree rather than the current text. + String? _dslError; + + /// Reparsing the whole tree and rebuilding the preview on every keystroke is + /// wasted work on the larger examples, so coalesce bursts of typing. + Timer? _previewDebounce; + static const _previewDelay = Duration(milliseconds: 200); + + bool get _isDart => widget.language == CodeLanguage.dart; + + static String _formatJson(Map json) { + return const JsonEncoder.withIndent(' ').convert(json); + } + + @override + void initState() { + super.initState(); + final state = context.read().state; + if (_isDart) { + _baselineText = state.dartCode; + _baselineIsPristine = true; + } else { + // The current jsonData carries in-progress edits across language/view + // switches; it equals the entry's JSON when unedited. + var text = ''; + try { + text = _formatJson(state.jsonData); + } catch (_) {} + _baselineText = text; + _baselineIsPristine = !state.edited; + } + _lastText = _baselineText; + _controller = CodeLineEditingController.fromText(_baselineText); + _controller.addListener(_onEditorChanged); + _findController = CodeFindController(_controller); + } + + void _onEditorChanged() { + final text = _controller.text; + if (text == _lastText) return; + _lastText = text; + final cubit = context.read(); + if (_baselineIsPristine) { + cubit.setEdited(text != _baselineText); + } else if (text != _baselineText) { + cubit.setEdited(true); + } + // The dirty indicator above stays immediate; only the parse and re-render + // wait for typing to settle. + _previewDebounce?.cancel(); + _previewDebounce = Timer(_previewDelay, () => _refreshPreview(text)); + } + + /// Dart goes through the DSL subset parser, JSON is decoded directly; either + /// way the preview renders from the resulting widget map. When the source + /// can't be converted the last good preview stays put. + void _refreshPreview(String text) { + if (!mounted) return; + final result = parseEditorSource(text, isDart: _isDart); + final json = result.json; + if (json != null) context.read().updateJsonData(json); + _setDslError(result.message); + } + + void _setDslError(String? message) { + if (!mounted || _dslError == message) return; + setState(() => _dslError = message); + } + + @override + void dispose() { + _previewDebounce?.cancel(); + _controller.removeListener(_onEditorChanged); + _controller.dispose(); + super.dispose(); + } + + /// VS Code Dark+–style colors, same as the Stac Console editor. + Map _editorTheme() { + final font = _defaultFont; + const foreground = Color(0xFFD4D4D4); + const string = Color(0xFFCE9178); + const key = Color(0xFF9CDCFE); + const number = Color(0xFFB5CEA8); + const keywordLiteral = Color(0xFF569CD6); + const comment = Color(0xFF6A9955); + const bracketYellow = Color(0xFFD7BA7D); + return { + 'root': font.copyWith(color: foreground), + 'punctuation': font.copyWith(color: bracketYellow), + 'bracket': font.copyWith(color: bracketYellow), + 'brace': font.copyWith(color: bracketYellow), + 'tag': font.copyWith(color: bracketYellow), + 'comment': font.copyWith(color: comment), + 'quote': font.copyWith(color: comment), + 'keyword': font.copyWith(color: keywordLiteral), + 'name': font.copyWith(color: key), + 'literal': font.copyWith(color: keywordLiteral), + 'string': font.copyWith(color: string), + 'number': font.copyWith(color: number), + 'property': font.copyWith(color: key), + 'attr': font.copyWith(color: key), + // Dart-specific scopes (annotations, types, function names). + 'title': font.copyWith(color: const Color(0xFFDCDCAA)), + 'title.class': font.copyWith(color: const Color(0xFF4EC9B0)), + 'title.function': font.copyWith(color: const Color(0xFFDCDCAA)), + 'built_in': font.copyWith(color: const Color(0xFF4EC9B0)), + 'meta': font.copyWith(color: key), + 'variable': font.copyWith(color: key), + 'params': font.copyWith(color: foreground), + }; + } + + @override + Widget build(BuildContext context) { + return Column( + children: [ + SectionNav( + child: Row( + children: [ + const SizedBox(width: 12), + Expanded( + child: Row( + children: [ + BlocBuilder( + buildWhen: (previous, current) => + previous.edited != current.edited, + builder: (context, state) => PhosphorIcon( + PhosphorIcons.bracketsAngle, + size: 14, + color: state.edited && !_isDart + ? context.colors.warning + : context.colors.onBackground, + ), + ), + const SizedBox(width: 6), + Flexible( + child: Text( + '${widget.entryId}.${_isDart ? 'dart' : 'json'}', + overflow: TextOverflow.ellipsis, + style: TextStyle( + fontSize: 14, + height: 1.5, + color: context.colors.onBackground, + ), + ), + ), + if (widget.showLanguageToggle) ...[ + const SizedBox(width: 12), + _LanguageToggle(language: widget.language), + ], + ], + ), + ), + const SizedBox(width: 12), + _NavIcon( + icon: PhosphorIcons.magnifyingGlass, + tooltip: 'Search', + onTap: () { + _findController.findMode(); + _findController.focusOnFindInput(); + }, + ), + if (!_isDart) ...[ + const SizedBox(width: 12), + const NavDivider(), + const SizedBox(width: 12), + _NavIcon( + icon: PhosphorIcons.arrowCounterClockwise, + tooltip: 'Undo', + onTap: () => _controller.undo(), + ), + const SizedBox(width: 12), + _NavIcon( + icon: PhosphorIcons.arrowClockwise, + tooltip: 'Redo', + onTap: () => _controller.redo(), + ), + ], + const SizedBox(width: 12), + const NavDivider(), + const SizedBox(width: 12), + _CopyCodeButton( + onCopy: () => Clipboard.setData( + ClipboardData(text: _controller.text), + ), + ), + const SizedBox(width: 12), + ], + ), + ), + Expanded( + child: CodeEditor( + controller: _controller, + findController: _findController, + readOnly: false, + style: CodeEditorStyle( + fontFamily: 'JetBrainsMono', + fontSize: 13, + fontHeight: 1.5, + codeTheme: CodeHighlightTheme( + languages: _isDart + ? {'dart': CodeHighlightThemeMode(mode: _dartHighlightMode)} + : {'json': CodeHighlightThemeMode(mode: langJson)}, + theme: _editorTheme(), + ), + ), + wordWrap: false, + indicatorBuilder: ( + context, + editingController, + chunkController, + notifier, + ) { + return Row( + children: [ + const SizedBox(width: 12), + DefaultCodeLineNumber( + controller: editingController, + notifier: notifier, + textStyle: _defaultFont.copyWith( + color: Colors.white.withValues(alpha: 0.5), + ), + focusedTextStyle: _defaultFont, + ), + const SizedBox(width: 2), + DefaultCodeChunkIndicator( + width: 20, + controller: chunkController, + notifier: notifier, + ), + ], + ); + }, + findBuilder: (context, controller, readOnly) => CodeFindPanelView( + controller: controller, + readOnly: readOnly, + ), + toolbarController: const _EditorContextMenuController(), + ), + ), + if (_isDart && _dslError != null) _dslNotice(context, _dslError!), + ], + ); + } + + /// Status strip shown when the Dart source can't be turned into JSON, so it's + /// obvious the preview has stopped following the editor. + Widget _dslNotice(BuildContext context, String message) { + return Container( + width: double.infinity, + padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), + color: const Color(0xFF3A2D00), + child: Row( + children: [ + const PhosphorIcon( + PhosphorIcons.warningDiamond, + size: 14, + color: Color(0xFFE2C08D), + ), + const SizedBox(width: 8), + Expanded( + child: Text( + 'Preview not updated — $message', + style: const TextStyle( + fontSize: 11, + height: 1.4, + color: Color(0xFFE2C08D), + ), + ), + ), + ], + ), + ); + } +} + +/// Segmented Dart/JSON switcher: Dart shows the Stac DSL source, JSON the +/// deployed output that `stac build` generates from it. +class _LanguageToggle extends StatelessWidget { + const _LanguageToggle({required this.language}); + + final CodeLanguage language; + + @override + Widget build(BuildContext context) { + return Container( + height: 24, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(2), + border: Border.all(color: context.colors.outline2), + ), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + _segment(context, label: 'Dart', value: CodeLanguage.dart), + Container(width: 1, color: context.colors.outline2), + _segment(context, label: 'JSON', value: CodeLanguage.json), + ], + ), + ); + } + + Widget _segment( + BuildContext context, { + required String label, + required CodeLanguage value, + }) { + final selected = language == value; + return Tooltip( + message: value == CodeLanguage.dart + ? 'Stac DSL source (compiled to JSON by stac build)' + : 'Deployed JSON output', + child: InkWell( + onTap: () => context.read().setCodeLanguage(value), + hoverColor: context.colors.surfaceVariant, + child: Container( + padding: const EdgeInsets.symmetric(horizontal: 8), + color: selected ? context.colors.surfaceVariant : null, + child: Center( + child: Text( + label, + style: TextStyle( + fontSize: 12, + fontWeight: selected ? FontWeight.w600 : FontWeight.w400, + height: 1.5, + fontVariations: [ + FontVariation('wght', selected ? 600 : 400), + ], + color: selected + ? context.colors.onBackground + : Colors.white.withValues(alpha: 0.5), + ), + ), + ), + ), + ), + ); + } +} + +class _NavIcon extends StatelessWidget { + const _NavIcon({ + required this.icon, + required this.tooltip, + required this.onTap, + }); + + final IconData icon; + final String tooltip; + final VoidCallback onTap; + + @override + Widget build(BuildContext context) { + return Tooltip( + message: tooltip, + child: InkWell( + onTap: onTap, + hoverColor: context.colors.surfaceVariant, + child: PhosphorIcon( + icon, + size: 18, + color: context.colors.onBackground2, + ), + ), + ); + } +} + +class _CopyCodeButton extends StatefulWidget { + const _CopyCodeButton({required this.onCopy}); + + final VoidCallback onCopy; + + @override + State<_CopyCodeButton> createState() => _CopyCodeButtonState(); +} + +class _CopyCodeButtonState extends State<_CopyCodeButton> { + bool _copied = false; + + void _handleTap() { + widget.onCopy(); + setState(() => _copied = true); + Future.delayed(const Duration(seconds: 2), () { + if (mounted) setState(() => _copied = false); + }); + } + + @override + Widget build(BuildContext context) { + return InkWell( + onTap: _handleTap, + hoverColor: context.colors.surfaceVariant, + child: Container( + height: 24, + padding: const EdgeInsets.symmetric(horizontal: 10), + decoration: BoxDecoration( + color: context.colors.surfaceVariant, + borderRadius: BorderRadius.circular(2), + border: Border.all(color: context.colors.outline2), + ), + child: Center( + child: Text( + _copied ? 'Copied!' : 'Copy Code', + style: TextStyle( + fontSize: 12, + fontWeight: FontWeight.w600, + height: 1.5, + fontVariations: const [FontVariation('wght', 600)], + color: _copied + ? context.colors.secondary + : context.colors.onBackground2, + ), + ), + ), + ), + ); + } +} diff --git a/stac_playground/lib/app/widgets/find_panel.dart b/stac_playground/lib/app/widgets/find_panel.dart new file mode 100644 index 00000000..b3b1fabf --- /dev/null +++ b/stac_playground/lib/app/widgets/find_panel.dart @@ -0,0 +1,300 @@ +import 'dart:math'; + +import 'package:flutter/material.dart'; +import 'package:phosphoricons_flutter/phosphoricons_flutter.dart'; +import 'package:re_editor/re_editor.dart'; +import 'package:stac_playground/theme/app_theme.dart'; + +const EdgeInsetsGeometry _kDefaultFindMargin = EdgeInsets.only(right: 10); +const double _kDefaultFindPanelWidth = 360; +const double _kDefaultFindPanelHeight = 40; +const double _kDefaultReplacePanelHeight = _kDefaultFindPanelHeight * 2; +const double _kDefaultFindIconSize = 18; +const double _kDefaultFindIconWidth = 30; +const double _kDefaultFindIconHeight = 30; +const double _kDefaultFindInputFontSize = 14; +const double _kDefaultFindResultFontSize = 12; +const EdgeInsetsGeometry _kDefaultFindPadding = EdgeInsets.all(0); +const EdgeInsetsGeometry _kDefaultFindInputContentPadding = + EdgeInsets.symmetric(horizontal: 5); + +const Color _kMutedText = Color(0x80FFFFFF); +const Color _kHairline = Color(0x26FFFFFF); + +/// Find/replace panel for the code editor, styled after the Stac Console. +class CodeFindPanelView extends StatelessWidget implements PreferredSizeWidget { + final CodeFindController controller; + final EdgeInsetsGeometry margin; + final bool readOnly; + final Color? iconColor; + final Color? iconSelectedColor; + final double iconSize; + final double inputFontSize; + final double resultFontSize; + final Color? inputTextColor; + final Color? resultFontColor; + final EdgeInsetsGeometry padding; + final InputDecoration decoration; + + const CodeFindPanelView({ + super.key, + required this.controller, + this.margin = _kDefaultFindMargin, + required this.readOnly, + this.iconSelectedColor, + this.iconColor, + this.iconSize = _kDefaultFindIconSize, + this.inputFontSize = _kDefaultFindInputFontSize, + this.resultFontSize = _kDefaultFindResultFontSize, + this.inputTextColor, + this.resultFontColor, + this.padding = _kDefaultFindPadding, + this.decoration = const InputDecoration( + filled: true, + fillColor: Colors.transparent, + isDense: true, + isCollapsed: true, + contentPadding: _kDefaultFindInputContentPadding, + border: InputBorder.none, + enabledBorder: InputBorder.none, + focusedBorder: InputBorder.none, + disabledBorder: InputBorder.none, + ), + }); + + @override + Size get preferredSize => Size( + double.infinity, + controller.value == null + ? 0 + : ((controller.value!.replaceMode + ? _kDefaultReplacePanelHeight + : _kDefaultFindPanelHeight) + + margin.vertical)); + + @override + Widget build(BuildContext context) { + if (controller.value == null) { + return const SizedBox(width: 0, height: 0); + } + return Container( + width: preferredSize.width, + color: const Color(0x800B0B0D), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + _buildFindInputView(context), + if (controller.value!.replaceMode) _buildReplaceInputView(context), + Container( + height: 1, + color: context.colors.outline, + ), + ], + ), + ); + } + + Widget _buildFindInputView(BuildContext context) { + final CodeFindValue value = controller.value!; + final int resultsCount = value.result?.matches.length ?? 0; + final String result = + '$resultsCount ${resultsCount == 1 ? 'result' : 'results'}'; + return Container( + width: double.maxFinite, + padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 4), + height: _kDefaultFindPanelHeight, + child: Row( + children: [ + const PhosphorIcon( + PhosphorIconsRegular.magnifyingGlass, + size: 14, + color: _kMutedText, + ), + const SizedBox(width: 6), + Expanded( + child: _buildTextField( + context: context, + controller: controller.findInputController, + focusNode: controller.findInputFocusNode, + ), + ), + const SizedBox(width: 12), + Container(width: 1, height: 10, color: _kHairline), + const SizedBox(width: 12), + _buildCheckText( + context: context, + text: 'Aa', + checked: value.option.caseSensitive, + onPressed: () => controller.toggleCaseSensitive(), + tooltip: 'Case sensitive', + ), + const SizedBox(width: 12), + _buildCheckText( + context: context, + text: '.*', + checked: value.option.regex, + onPressed: () => controller.toggleRegex(), + tooltip: 'Regex', + ), + const SizedBox(width: 12), + Container(width: 1, height: 10, color: _kHairline), + const SizedBox(width: 12), + Text( + result, + style: TextStyle( + fontSize: resultFontSize, + height: 1.5, + color: resultsCount == 0 + ? _kMutedText + : (resultFontColor ?? context.colors.onBackground), + ), + ), + const SizedBox(width: 12), + Container(width: 1, height: 10, color: _kHairline), + const SizedBox(width: 12), + _buildIconButton( + onPressed: + value.result == null ? null : () => controller.previousMatch(), + icon: PhosphorIcon(PhosphorIconsRegular.arrowUp, size: iconSize), + tooltip: 'Previous match', + ), + const SizedBox(width: 12), + _buildIconButton( + onPressed: + value.result == null ? null : () => controller.nextMatch(), + icon: PhosphorIcon(PhosphorIconsRegular.arrowDown, size: iconSize), + tooltip: 'Next match', + ), + const SizedBox(width: 12), + Container(width: 1, height: 10, color: _kHairline), + const SizedBox(width: 12), + _buildIconButton( + onPressed: () => controller.close(), + icon: PhosphorIcon(PhosphorIconsRegular.x, size: iconSize), + tooltip: 'Close', + ), + ], + ), + ); + } + + Widget _buildReplaceInputView(BuildContext context) { + return Row( + children: [ + SizedBox( + width: _kDefaultFindPanelWidth / 1.75, + height: _kDefaultFindPanelHeight, + child: _buildTextField( + context: context, + controller: controller.replaceInputController, + focusNode: controller.replaceInputFocusNode, + ), + ), + _buildIconButton( + onPressed: controller.value!.result == null + ? null + : () => controller.replaceMatch(), + icon: PhosphorIcon(PhosphorIconsRegular.check, size: iconSize), + tooltip: 'Replace', + ), + _buildIconButton( + onPressed: controller.value!.result == null + ? null + : () => controller.replaceAllMatches(), + icon: PhosphorIcon(PhosphorIconsRegular.checks, size: iconSize), + tooltip: 'Replace all', + ), + ], + ); + } + + Widget _buildTextField({ + required BuildContext context, + required TextEditingController controller, + required FocusNode focusNode, + }) { + return Padding( + padding: padding, + child: TextField( + maxLines: 1, + focusNode: focusNode, + textAlignVertical: TextAlignVertical.center, + style: TextStyle( + fontSize: inputFontSize, + height: 1.5, + color: inputTextColor ?? context.colors.onBackground, + ), + decoration: decoration.copyWith( + hintText: 'Search this file', + hintStyle: TextStyle( + fontSize: inputFontSize, + height: 1.5, + color: _kMutedText, + ), + contentPadding: const EdgeInsets.fromLTRB(6, 8, 6, 6), + constraints: const BoxConstraints( + minHeight: _kDefaultFindIconHeight, + maxHeight: _kDefaultFindIconHeight, + ), + ), + controller: controller, + ), + ); + } + + Widget _buildCheckText({ + required BuildContext context, + required String text, + required bool checked, + required VoidCallback onPressed, + String? tooltip, + }) { + final Color selectedColor = iconSelectedColor ?? context.colors.secondary; + final Widget content = Material( + type: MaterialType.transparency, + child: InkWell( + onTap: onPressed, + mouseCursor: SystemMouseCursors.click, + hoverColor: Colors.white.withValues(alpha: 0.06), + splashColor: Colors.white.withValues(alpha: 0.10), + customBorder: + RoundedRectangleBorder(borderRadius: BorderRadius.circular(4)), + child: SizedBox( + width: 18, + height: 18, + child: Center( + child: Text( + text, + textAlign: TextAlign.center, + style: TextStyle( + fontSize: inputFontSize, + height: 1.0, + color: checked ? selectedColor : _kMutedText, + ), + ), + ), + ), + ), + ); + return tooltip == null + ? content + : Tooltip(message: tooltip, child: content); + } + + Widget _buildIconButton({ + required Widget icon, + VoidCallback? onPressed, + String? tooltip, + }) { + return IconButton( + onPressed: onPressed, + icon: icon, + constraints: const BoxConstraints( + maxWidth: _kDefaultFindIconWidth, + maxHeight: _kDefaultFindIconHeight, + ), + tooltip: tooltip, + splashRadius: max(_kDefaultFindIconWidth, _kDefaultFindIconHeight) / 2, + ); + } +} diff --git a/stac_playground/lib/app/widgets/index_panel.dart b/stac_playground/lib/app/widgets/index_panel.dart new file mode 100644 index 00000000..396fe5e2 --- /dev/null +++ b/stac_playground/lib/app/widgets/index_panel.dart @@ -0,0 +1,341 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:phosphoricons_flutter/phosphoricons_flutter.dart'; +import 'package:stac/stac.dart'; +import 'package:stac_playground/app/cubit/home_cubit.dart'; +import 'package:stac_playground/app/cubit/home_state.dart'; +import 'package:stac_playground/data/playground_entry.dart'; +import 'package:stac_playground/theme/app_theme.dart'; + +/// Console-styled search input filtering the entry index. +class _IndexSearchField extends StatelessWidget { + @override + Widget build(BuildContext context) { + return Container( + height: 28, + decoration: BoxDecoration( + color: context.colors.surfaceVariant, + borderRadius: BorderRadius.circular(2), + border: Border.all(color: context.colors.outline2), + ), + child: Row( + children: [ + const SizedBox(width: 8), + PhosphorIcon( + PhosphorIcons.magnifyingGlass, + size: 12, + color: context.colors.onSurfaceVariant, + ), + const SizedBox(width: 6), + Expanded( + child: TextField( + onChanged: (v) => context.read().setQuery(v), + style: TextStyle( + fontSize: 12, + height: 1.5, + color: context.colors.onBackground, + ), + decoration: InputDecoration( + isCollapsed: true, + border: InputBorder.none, + hintText: 'Search', + hintStyle: TextStyle( + fontSize: 12, + height: 1.5, + color: context.colors.onBackground3, + ), + ), + ), + ), + const SizedBox(width: 8), + ], + ), + ); + } +} + +/// Small uppercase section label between index groups. +class _GroupLabel extends StatelessWidget { + const _GroupLabel(this.text); + + final String text; + + @override + Widget build(BuildContext context) { + return Padding( + padding: const EdgeInsets.fromLTRB(12, 10, 12, 4), + child: Text( + text, + style: TextStyle( + fontSize: 9, + fontWeight: FontWeight.w500, + height: 1.3, + letterSpacing: 0.72, + fontVariations: const [FontVariation('wght', 500)], + color: Colors.white.withValues(alpha: 0.5), + ), + ), + ); + } +} + +/// The 280px screen index: playground title plus the list of sample screens. +class IndexPanel extends StatelessWidget { + const IndexPanel({super.key}); + + @override + Widget build(BuildContext context) { + return Container( + width: 280, + decoration: BoxDecoration( + color: context.colors.background, + border: Border(right: BorderSide(color: context.colors.outline)), + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: const EdgeInsets.all(12), + child: Text( + 'Playground', + style: TextStyle( + fontSize: 14, + fontWeight: FontWeight.w600, + height: 1.5, + fontVariations: const [FontVariation('wght', 600)], + color: context.colors.onBackground, + ), + ), + ), + Padding( + padding: const EdgeInsets.fromLTRB(12, 0, 12, 12), + child: BlocBuilder( + buildWhen: (previous, current) => previous.view != current.view, + builder: (context, state) => _ViewToggle(view: state.view), + ), + ), + Padding( + padding: const EdgeInsets.fromLTRB(12, 0, 12, 8), + child: _IndexSearchField(), + ), + Expanded( + child: BlocBuilder( + buildWhen: (previous, current) => + previous.selectedEntry.id != current.selectedEntry.id || + previous.edited != current.edited || + previous.query != current.query, + builder: (context, state) { + final query = state.query.toLowerCase(); + bool matches(PlaygroundEntry e) => + query.isEmpty || + e.id.contains(query) || + e.title.toLowerCase().contains(query); + final screens = playgroundEntries + .where((e) => e.category == EntryCategory.screen) + .where(matches) + .toList(); + final components = playgroundEntries + .where((e) => e.category == EntryCategory.component) + .where(matches) + .toList(); + Widget row(PlaygroundEntry e) => _IndexRow( + iconName: e.icon, + iconType: e.iconType, + label: e.id, + selected: state.selectedEntry.id == e.id, + showChangeDot: + state.selectedEntry.id == e.id && state.edited, + onTap: () => context.read().selectEntry(e), + ); + return ListView( + padding: const EdgeInsets.only(bottom: 12), + children: [ + if (screens.isNotEmpty) ...[ + const _GroupLabel('EXAMPLES'), + ...screens.map(row), + ], + if (components.isNotEmpty) ...[ + const _GroupLabel('WIDGETS'), + ...components.map(row), + ], + ], + ); + }, + ), + ), + ], + ), + ); + } +} + +/// Switches the playground between the live preview layout and the +/// side-by-side Dart/JSON code view. +class _ViewToggle extends StatelessWidget { + const _ViewToggle({required this.view}); + + final PlaygroundView view; + + @override + Widget build(BuildContext context) { + return Container( + height: 28, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(2), + border: Border.all(color: context.colors.outline2), + ), + child: Row( + // Stretch segments to the toggle's full height so the selected + // segment's background fills it instead of leaving bars top and bottom. + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + _segment( + context, + icon: PhosphorIcons.eye, + label: 'Preview', + value: PlaygroundView.preview, + tooltip: 'Code editor with live preview', + ), + Container(width: 1, color: context.colors.outline2), + _segment( + context, + icon: PhosphorIcons.columns, + label: 'Code Diff', + value: PlaygroundView.codeDiff, + tooltip: 'Dart DSL and JSON side by side', + ), + ], + ), + ); + } + + Widget _segment( + BuildContext context, { + required IconData icon, + required String label, + required PlaygroundView value, + required String tooltip, + }) { + final selected = view == value; + final color = selected + ? context.colors.onBackground + : Colors.white.withValues(alpha: 0.5); + return Expanded( + child: Tooltip( + message: tooltip, + child: InkWell( + onTap: () => context.read().setView(value), + hoverColor: context.colors.surfaceVariant, + child: Container( + color: selected ? context.colors.surfaceVariant : null, + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + PhosphorIcon(icon, size: 12, color: color), + const SizedBox(width: 6), + Text( + label, + style: TextStyle( + fontSize: 12, + fontWeight: selected ? FontWeight.w600 : FontWeight.w400, + height: 1.5, + fontVariations: [ + FontVariation('wght', selected ? 600 : 400), + ], + color: color, + ), + ), + ], + ), + ), + ), + ), + ); + } +} + +/// `#AARRGGBB` string for Stac's color parser (the icon renderer takes a hex +/// string, not a Flutter [Color]). +String _hex(Color color) => + '#${color.toARGB32().toRadixString(16).padLeft(8, '0')}'; + +class _IndexRow extends StatelessWidget { + const _IndexRow({ + required this.iconName, + required this.iconType, + required this.label, + this.selected = false, + this.showChangeDot = false, + this.onTap, + }); + + /// Icon name resolved through Stac's icon parser, matching the mobile list. + final String? iconName; + final String iconType; + final String label; + final bool selected; + final bool showChangeDot; + final VoidCallback? onTap; + + @override + Widget build(BuildContext context) { + final color = + selected ? context.colors.onBackground : context.colors.onBackground2; + + return Padding( + padding: const EdgeInsets.only(bottom: 2), + child: ListTile( + onTap: onTap, + selected: selected, + selectedTileColor: context.colors.surfaceVariant, + hoverColor: context.colors.surfaceVariant, + // The index is an IDE-style file list, so it sits far tighter than a + // stock tile: no vertical padding, and only as tall as the label. + dense: true, + visualDensity: VisualDensity.compact, + minTileHeight: 24, + minVerticalPadding: 0, + minLeadingWidth: 14, + horizontalTitleGap: 8, + contentPadding: const EdgeInsets.symmetric(horizontal: 12), + leading: SizedBox( + width: 14, + height: 14, + child: iconName == null + ? PhosphorIcon( + PhosphorIcons.bracketsAngle, + size: 12, + color: color, + ) + : Stac.fromJson({ + 'type': 'icon', + 'iconType': iconType, + 'icon': iconName, + 'size': 14, + 'color': _hex(color), + }, context), + ), + title: Text( + label, + overflow: TextOverflow.ellipsis, + style: TextStyle( + fontSize: 14, + fontWeight: FontWeight.w400, + height: 1.5, + color: color, + ), + ), + trailing: !showChangeDot + ? null + : Container( + width: 5, + height: 5, + decoration: BoxDecoration( + color: context.colors.warning, + borderRadius: BorderRadius.circular(20), + ), + ), + ), + ); + } +} diff --git a/stac_playground/lib/app/widgets/section_nav.dart b/stac_playground/lib/app/widgets/section_nav.dart new file mode 100644 index 00000000..3f5b863a --- /dev/null +++ b/stac_playground/lib/app/widgets/section_nav.dart @@ -0,0 +1,37 @@ +import 'package:flutter/material.dart'; +import 'package:stac_playground/theme/app_theme.dart'; + +/// 44px header bar used at the top of the editor and preview panes. +class SectionNav extends StatelessWidget { + const SectionNav({super.key, required this.child}); + + final Widget child; + + static const double height = 44; + + @override + Widget build(BuildContext context) { + return Container( + height: height, + decoration: BoxDecoration( + color: const Color(0x800B0B0D), + border: Border(bottom: BorderSide(color: context.colors.outline)), + ), + child: child, + ); + } +} + +/// Short vertical hairline separating groups of actions in a [SectionNav]. +class NavDivider extends StatelessWidget { + const NavDivider({super.key}); + + @override + Widget build(BuildContext context) { + return Container( + width: 1, + height: 10, + color: Colors.white.withValues(alpha: 0.15), + ); + } +} diff --git a/stac_playground/lib/data/component_entries.dart b/stac_playground/lib/data/component_entries.dart new file mode 100644 index 00000000..30bfdf24 --- /dev/null +++ b/stac_playground/lib/data/component_entries.dart @@ -0,0 +1,787 @@ +import 'package:stac_playground/data/playground_entry.dart'; + +/// Gallery component entries, generated from the Stac Gallery listing. +const List componentEntries = [ + PlaygroundEntry( + id: 'app_bar', + title: 'Stac Appbar', + description: + 'Displays information and actions relating to the current screen', + jsonAsset: 'assets/json/app_bar_example.json', + dartAsset: 'lib/dsl/app_bar.dart', + icon: 'app_fill', + iconType: 'cupertino', + category: EntryCategory.component, + ), + PlaygroundEntry( + id: 'auto_complete', + title: 'Stac AutoComplete', + description: 'Create a auto complete widget with list of strings', + jsonAsset: 'assets/json/auto_complete_example.json', + dartAsset: 'lib/dsl/auto_complete.dart', + icon: 'done_all', + iconType: 'material', + category: EntryCategory.component, + ), + PlaygroundEntry( + id: 'tool_tip', + title: 'Stac Tooltip', + description: 'Create a widget with tooltip', + jsonAsset: 'assets/json/tool_tip_example.json', + dartAsset: 'lib/dsl/tool_tip.dart', + icon: 'info', + iconType: 'material', + category: EntryCategory.component, + ), + PlaygroundEntry( + id: 'badge', + title: 'Stac Badge', + description: 'Display small status descriptors, counts, or notifications', + jsonAsset: 'assets/json/badge_example.json', + dartAsset: 'lib/dsl/badge.dart', + icon: 'badge', + iconType: 'material', + category: EntryCategory.component, + ), + PlaygroundEntry( + id: 'bottom_nav_bar', + title: 'Stac Bottom Navigation', + description: 'Bottom navigation with cross-fading views', + jsonAsset: 'assets/json/bottom_nav_bar_example.json', + dartAsset: 'lib/dsl/bottom_nav_bar.dart', + icon: 'navigation', + iconType: 'material', + category: EntryCategory.component, + ), + PlaygroundEntry( + id: 'bottom_sheet', + title: 'Stac Bottom Sheet', + description: 'A Material Design Stac bottom sheet example', + jsonAsset: 'assets/json/bottom_sheet_example.json', + dartAsset: 'lib/dsl/bottom_sheet.dart', + icon: 'navigation', + iconType: 'material', + category: EntryCategory.component, + ), + PlaygroundEntry( + id: 'card', + title: 'Stac Card', + description: + 'Card is a sheet of material used to represent some related information', + jsonAsset: 'assets/json/card_example.json', + dartAsset: 'lib/dsl/card.dart', + icon: 'wallet_giftcard', + iconType: 'material', + category: EntryCategory.component, + ), + PlaygroundEntry( + id: 'carousel_view', + title: 'Stac Carousel View', + description: + 'The CarouselView presents a scrollable list of items, each of which can dynamically change size based on the chosen layout.', + jsonAsset: 'assets/json/carousel_view_example.json', + dartAsset: 'lib/dsl/carousel_view.dart', + icon: 'view_carousel', + iconType: 'material', + category: EntryCategory.component, + ), + PlaygroundEntry( + id: 'center', + title: 'Stac Center', + description: 'A widget that centers its child within itself.', + jsonAsset: 'assets/json/center_example.json', + dartAsset: 'lib/dsl/center.dart', + icon: 'format_align_center', + iconType: 'material', + category: EntryCategory.component, + ), + PlaygroundEntry( + id: 'drawer', + title: 'Stac Drawer', + description: + 'A Material Design panel that slides in horizontally from the edge of a Scaffold', + jsonAsset: 'assets/json/drawer_example.json', + dartAsset: 'lib/dsl/drawer.dart', + icon: 'menu', + iconType: 'material', + category: EntryCategory.component, + ), + PlaygroundEntry( + id: 'align', + title: 'Stac Align', + description: + 'widget that is used to align its child within itself and optionally sizes itself based on the child\'s size', + jsonAsset: 'assets/json/align_example.json', + dartAsset: 'lib/dsl/align.dart', + icon: 'format_align_center', + iconType: 'material', + category: EntryCategory.component, + ), + PlaygroundEntry( + id: 'column', + title: 'Stac Column', + description: 'This is a stac column widget.', + jsonAsset: 'assets/json/column_example.json', + dartAsset: 'lib/dsl/column.dart', + icon: 'table_rows', + iconType: 'material', + category: EntryCategory.component, + ), + PlaygroundEntry( + id: 'dialog', + title: 'Stac Dialog', + description: + 'Informs the user about situations that require acknowledgement', + jsonAsset: 'assets/json/dialog_example.json', + dartAsset: 'lib/dsl/dialog.dart', + icon: 'add_alert', + iconType: 'material', + category: EntryCategory.component, + ), + PlaygroundEntry( + id: 'stack', + title: 'Stac Stack', + description: 'This is a stac stack widget.', + jsonAsset: 'assets/json/stack_example.json', + dartAsset: 'lib/dsl/stack.dart', + icon: 'layers', + iconType: 'material', + category: EntryCategory.component, + ), + PlaygroundEntry( + id: 'container', + title: 'Stac Container', + description: + 'Container combines common painting, positioning, and sizing widgets', + jsonAsset: 'assets/json/container_example.json', + dartAsset: 'lib/dsl/container.dart', + icon: 'apartment', + iconType: 'material', + category: EntryCategory.component, + ), + PlaygroundEntry( + id: 'elevated_button', + title: 'Stac Elevated Button', + description: 'A Material Design elevated button', + jsonAsset: 'assets/json/elevated_button_example.json', + dartAsset: 'lib/dsl/elevated_button.dart', + icon: 'smart_button', + iconType: 'material', + category: EntryCategory.component, + ), + PlaygroundEntry( + id: 'floating_action_button', + title: 'Stac Floating Action Button', + description: + 'A floating action button is a circular icon button that hovers over content to promote a primary action in the application', + jsonAsset: 'assets/json/floating_action_button_example.json', + dartAsset: 'lib/dsl/floating_action_button.dart', + icon: 'blur_circular_rounded', + iconType: 'material', + category: EntryCategory.component, + ), + PlaygroundEntry( + id: 'icon', + title: 'Stac Icon', + description: + 'An icon button is a picture printed on a Material widget that reacts to touches by filling with color', + jsonAsset: 'assets/json/icon_example.json', + dartAsset: 'lib/dsl/icon.dart', + icon: 'radio_button_checked', + iconType: 'material', + category: EntryCategory.component, + ), + PlaygroundEntry( + id: 'icon_button', + title: 'Stac Icon Button', + description: 'A Material Design icon', + jsonAsset: 'assets/json/icon_button_example.json', + dartAsset: 'lib/dsl/icon_button.dart', + icon: 'heart_solid', + iconType: 'cupertino', + category: EntryCategory.component, + ), + PlaygroundEntry( + id: 'image', + title: 'Stac Image', + description: 'A widget that displays an image', + jsonAsset: 'assets/json/image_example.json', + dartAsset: 'lib/dsl/image.dart', + icon: 'image', + iconType: 'material', + category: EntryCategory.component, + ), + PlaygroundEntry( + id: 'inkwell', + title: 'Stac InkWell', + description: 'Creates an Inkwell effect when tapped', + jsonAsset: 'assets/json/inkwell_example.json', + dartAsset: 'lib/dsl/inkwell.dart', + icon: 'hand_draw', + iconType: 'cupertino', + category: EntryCategory.component, + ), + PlaygroundEntry( + id: 'list_tile', + title: 'Stac List Tile', + description: + 'A single fixed-height row that typically contains some text as well as a leading or trailing icon', + jsonAsset: 'assets/json/list_tile_example.json', + dartAsset: 'lib/dsl/list_tile.dart', + icon: 'table_rows_sharp', + iconType: 'material', + category: EntryCategory.component, + ), + PlaygroundEntry( + id: 'list_view', + title: 'Stac List View', + description: 'A scrollable, linear list of widgets', + jsonAsset: 'assets/json/list_view_example.json', + dartAsset: 'lib/dsl/list_view.dart', + icon: 'view_list', + iconType: 'material', + category: EntryCategory.component, + ), + PlaygroundEntry( + id: 'navigation', + title: 'Stac Navigation', + description: + 'Shows examples of different types of navigation types and styles in Stac', + jsonAsset: 'assets/json/navigation_example.json', + dartAsset: 'lib/dsl/navigation.dart', + icon: 'navigation', + iconType: 'material', + category: EntryCategory.component, + ), + PlaygroundEntry( + id: 'navigation_bar', + title: 'Stac Navigation Bar', + description: 'Material 3 navigation bar with selectable destinations', + jsonAsset: 'assets/json/navigation_bar_example.json', + dartAsset: 'lib/dsl/navigation_bar.dart', + icon: 'navigation', + iconType: 'material', + category: EntryCategory.component, + ), + PlaygroundEntry( + id: 'outlined_button', + title: 'Stac Outlined Button', + description: + 'A Material Design outlined button, essentially a TextButton with an outlined border', + jsonAsset: 'assets/json/outlined_button_example.json', + dartAsset: 'lib/dsl/outlined_button.dart', + icon: 'smart_button_outlined', + iconType: 'material', + category: EntryCategory.component, + ), + PlaygroundEntry( + id: 'padding', + title: 'Stac Padding', + description: 'A widget that insets its child by the given padding', + jsonAsset: 'assets/json/padding_example.json', + dartAsset: 'lib/dsl/padding.dart', + icon: 'padding', + iconType: 'material', + category: EntryCategory.component, + ), + PlaygroundEntry( + id: 'page_view', + title: 'Stac PageView', + description: + 'A Material Design widget that displays a horizontal row of pages', + jsonAsset: 'assets/json/page_view_example.json', + dartAsset: 'lib/dsl/page_view.dart', + icon: 'table_chart', + iconType: 'material', + category: EntryCategory.component, + ), + PlaygroundEntry( + id: 'refresh_indicator', + title: 'Stac Refresh Indicator', + description: + 'A widget that supports the Material "swipe to refresh" idiom.', + jsonAsset: 'assets/json/refresh_indicator_example.json', + dartAsset: 'lib/dsl/refresh_indicator.dart', + icon: 'refresh', + iconType: 'material', + category: EntryCategory.component, + ), + PlaygroundEntry( + id: 'row', + title: 'Stac Row', + description: 'Layout a list of child widgets in the horizontal direction', + jsonAsset: 'assets/json/row_example.json', + dartAsset: 'lib/dsl/row.dart', + icon: 'table_rows', + iconType: 'material', + category: EntryCategory.component, + ), + PlaygroundEntry( + id: 'scaffold', + title: 'Stac Scaffold', + description: 'Implements the basic Material Design visual layout structure', + jsonAsset: 'assets/json/scaffold_example.json', + dartAsset: 'lib/dsl/scaffold.dart', + icon: 'widgets', + iconType: 'material', + category: EntryCategory.component, + ), + PlaygroundEntry( + id: 'scroll_view', + title: 'Stac Scroll View', + description: 'A box in which a single widget can be scrolled', + jsonAsset: 'assets/json/scroll_view_example.json', + dartAsset: 'lib/dsl/scroll_view.dart', + icon: 'swipe_down', + iconType: 'material', + category: EntryCategory.component, + ), + PlaygroundEntry( + id: 'sized_box', + title: 'Stac Sizedbox', + description: + 'A box with a specified size. If given a child, this widget forces its child to have a specific dimensions.', + jsonAsset: 'assets/json/sized_box_example.json', + dartAsset: 'lib/dsl/sized_box.dart', + icon: 'space_bar', + iconType: 'material', + category: EntryCategory.component, + ), + PlaygroundEntry( + id: 'spacer', + title: 'Stac Spacer', + description: + 'Creates an adjustable, empty spacer that can be used to tune the spacing between widgets in a Flex container.', + jsonAsset: 'assets/json/spacer_example.json', + dartAsset: 'lib/dsl/spacer.dart', + icon: 'space_dashboard', + iconType: 'material', + category: EntryCategory.component, + ), + PlaygroundEntry( + id: 'switch', + title: 'Stac Switch', + description: + 'A switch component that can be toggled via dragging or tapping on it.', + jsonAsset: 'assets/json/switch_example.json', + dartAsset: 'lib/dsl/switch.dart', + icon: 'toggle_on', + iconType: 'material', + category: EntryCategory.component, + ), + PlaygroundEntry( + id: 'fractionally_sized_box', + title: 'Stac Fractionally Sizedbox', + description: + 'A widget that sizes its child to a fraction of the total available space.', + jsonAsset: 'assets/json/fractionally_sized_box_example.json', + dartAsset: 'lib/dsl/fractionally_sized_box.dart', + icon: 'space_bar', + iconType: 'material', + category: EntryCategory.component, + ), + PlaygroundEntry( + id: 'grid_view', + title: 'Stac GridView', + description: + 'A scrollable, 2D array of widgets that are created on demand.', + jsonAsset: 'assets/json/grid_view_example.json', + dartAsset: 'lib/dsl/grid_view.dart', + icon: 'grid_view', + iconType: 'material', + category: EntryCategory.component, + ), + PlaygroundEntry( + id: 'tab_bar', + title: 'Stac Tabbar', + description: + 'A Material Design widget that displays a horizontal row of tabs', + jsonAsset: 'assets/json/tab_bar_example.json', + dartAsset: 'lib/dsl/tab_bar.dart', + icon: 'table_chart', + iconType: 'material', + category: EntryCategory.component, + ), + PlaygroundEntry( + id: 'text', + title: 'Stac Text', + description: 'A run of text with a single style.', + jsonAsset: 'assets/json/text_example.json', + dartAsset: 'lib/dsl/text.dart', + icon: 'text_quote', + iconType: 'cupertino', + category: EntryCategory.component, + ), + PlaygroundEntry( + id: 'selectable_text', + title: 'Stac Selectable Text', + description: 'A run of text with a single style that can be selected', + jsonAsset: 'assets/json/selectable_text_example.json', + dartAsset: 'lib/dsl/selectable_text.dart', + icon: 'text_fields', + iconType: 'material', + category: EntryCategory.component, + ), + PlaygroundEntry( + id: 'text_button', + title: 'Stac Text Button', + description: + 'Text button displays an ink splash on press but does not lift', + jsonAsset: 'assets/json/text_button_example.json', + dartAsset: 'lib/dsl/text_button.dart', + icon: 'textbox', + iconType: 'cupertino', + category: EntryCategory.component, + ), + PlaygroundEntry( + id: 'text_field', + title: 'Stac Text Field', + description: 'Text fields allow users to type text into an app', + jsonAsset: 'assets/json/text_field_example.json', + dartAsset: 'lib/dsl/text_field.dart', + icon: 'text_fields', + iconType: 'material', + category: EntryCategory.component, + ), + PlaygroundEntry( + id: 'form', + title: 'Stac Form', + description: 'A Form Widget', + jsonAsset: 'assets/json/form_example.json', + dartAsset: 'lib/dsl/form.dart', + icon: 'blur_circular_rounded', + iconType: 'material', + category: EntryCategory.component, + ), + PlaygroundEntry( + id: 'web_view', + title: 'Stac WebView', + description: 'A Web View Widget', + jsonAsset: 'assets/json/web_view_example.json', + dartAsset: 'lib/dsl/web_view.dart', + icon: 'language', + iconType: 'material', + category: EntryCategory.component, + ), + PlaygroundEntry( + id: 'chip', + title: 'Chip', + description: + 'Chips are compact elements that represent an attribute, text, entity, or action', + jsonAsset: 'assets/json/chip_example.json', + dartAsset: 'lib/dsl/chip.dart', + icon: 'check_box_outline_blank', + iconType: 'material', + category: EntryCategory.component, + ), + PlaygroundEntry( + id: 'clip_oval', + title: 'ClipOval', + description: 'A widget that clips its child using an oval', + jsonAsset: 'assets/json/clip_oval_example.json', + dartAsset: 'lib/dsl/clip_oval.dart', + icon: 'crop_original', + iconType: 'material', + category: EntryCategory.component, + ), + PlaygroundEntry( + id: 'wrap', + title: 'Wrap', + description: 'A widget to creates a wrap layout', + jsonAsset: 'assets/json/wrap_example.json', + dartAsset: 'lib/dsl/wrap.dart', + icon: 'wrap_text', + iconType: 'material', + category: EntryCategory.component, + ), + PlaygroundEntry( + id: 'filled_button', + title: 'Filled Button', + description: 'A Material Design filled button', + jsonAsset: 'assets/json/filled_button_example.json', + dartAsset: 'lib/dsl/filled_button.dart', + icon: 'smart_button', + iconType: 'material', + category: EntryCategory.component, + ), + PlaygroundEntry( + id: 'table', + title: 'Stac Table', + description: 'A Material Design Rable widget', + jsonAsset: 'assets/json/table_example.json', + dartAsset: 'lib/dsl/table.dart', + icon: 'table_rows', + iconType: 'material', + category: EntryCategory.component, + ), + PlaygroundEntry( + id: 'divider', + title: 'Stac Divider', + description: 'A Material Design Divider widget', + jsonAsset: 'assets/json/divider_example.json', + dartAsset: 'lib/dsl/divider.dart', + icon: 'star', + iconType: 'material', + category: EntryCategory.component, + ), + PlaygroundEntry( + id: 'circular_progress_indicator', + title: 'Stac Circular Progress Indicator', + description: 'A Material Design Circular Progress Indicator widget', + jsonAsset: 'assets/json/circular_progress_indicator_example.json', + dartAsset: 'lib/dsl/circular_progress_indicator.dart', + icon: 'star', + iconType: 'material', + category: EntryCategory.component, + ), + PlaygroundEntry( + id: 'linear_progress_indicator', + title: 'Stac Linear Progress Indicator', + description: 'A Material Design Linear Progress Indicator widget', + jsonAsset: 'assets/json/linear_progress_indicator_example.json', + dartAsset: 'lib/dsl/linear_progress_indicator.dart', + icon: 'star', + iconType: 'material', + category: EntryCategory.component, + ), + PlaygroundEntry( + id: 'hero', + title: 'Stac Hero', + description: 'A Hero Widget', + jsonAsset: 'assets/json/hero_example.json', + dartAsset: 'lib/dsl/hero.dart', + icon: 'flight_takeoff', + iconType: 'material', + category: EntryCategory.component, + ), + PlaygroundEntry( + id: 'radio', + title: 'Stac Radio', + description: 'A Material Design Radio widget', + jsonAsset: 'assets/json/radio_example.json', + dartAsset: 'lib/dsl/radio.dart', + icon: 'radio', + iconType: 'material', + category: EntryCategory.component, + ), + PlaygroundEntry( + id: 'slider', + title: 'Stac Slider', + description: 'A Material Design Slider widget', + jsonAsset: 'assets/json/slider_example.json', + dartAsset: 'lib/dsl/slider.dart', + icon: 'slider_horizontal_3', + iconType: 'cupertino', + category: EntryCategory.component, + ), + PlaygroundEntry( + id: 'sliver_app_bar', + title: 'Stac Sliver App Bar', + description: 'A Material Design Sliver App Bar widget', + jsonAsset: 'assets/json/sliver_app_bar_example.json', + dartAsset: 'lib/dsl/sliver_app_bar.dart', + icon: 'app_fill', + iconType: 'cupertino', + category: EntryCategory.component, + ), + PlaygroundEntry( + id: 'sliver_grid', + title: 'Stac Sliver Grid', + description: 'A SliverGrid widget', + jsonAsset: 'assets/json/sliver_grid_example.json', + dartAsset: 'lib/dsl/sliver_grid.dart', + icon: 'app_fill', + iconType: 'cupertino', + category: EntryCategory.component, + ), + PlaygroundEntry( + id: 'sliver_padding', + title: 'Stac Sliver Padding', + description: 'A SliverPadding widget', + jsonAsset: 'assets/json/sliver_padding_example.json', + dartAsset: 'lib/dsl/sliver_padding.dart', + icon: 'app_fill', + iconType: 'cupertino', + category: EntryCategory.component, + ), + PlaygroundEntry( + id: 'sliver_safe_area', + title: 'Stac Sliver Safe Area', + description: 'A Sliver Safe Area widget', + jsonAsset: 'assets/json/sliver_safe_area_example.json', + dartAsset: 'lib/dsl/sliver_safe_area.dart', + icon: 'app_fill', + iconType: 'cupertino', + category: EntryCategory.component, + ), + PlaygroundEntry( + id: 'sliver_list', + title: 'Stac Sliver List', + description: 'A Sliver List widget', + jsonAsset: 'assets/json/sliver_list_example.json', + dartAsset: 'lib/dsl/sliver_list.dart', + icon: 'list', + iconType: 'material', + category: EntryCategory.component, + ), + PlaygroundEntry( + id: 'sliver_to_box_adapter', + title: 'Stac Sliver To Box Adapter', + description: 'A sliver that contains a single box widget.', + jsonAsset: 'assets/json/sliver_to_box_adapter_example.json', + dartAsset: 'lib/dsl/sliver_to_box_adapter.dart', + icon: 'app_fill', + iconType: 'cupertino', + category: EntryCategory.component, + ), + PlaygroundEntry( + id: 'sliver_fill_remaining', + title: 'Stac Sliver Fill Remaining', + description: 'A Sliver Fill Remaining widget.', + jsonAsset: 'assets/json/sliver_fill_remaining_example.json', + dartAsset: 'lib/dsl/sliver_fill_remaining.dart', + icon: 'app_fill', + iconType: 'cupertino', + category: EntryCategory.component, + ), + PlaygroundEntry( + id: 'sliver_opacity', + title: 'Stac Sliver Opacity', + description: 'A Material Design Sliver Opacity widget', + jsonAsset: 'assets/json/sliver_opacity_example.json', + dartAsset: 'lib/dsl/sliver_opacity.dart', + icon: 'app_fill', + iconType: 'cupertino', + category: EntryCategory.component, + ), + PlaygroundEntry( + id: 'sliver_visibility', + title: 'Stac Sliver Visibility', + description: 'A Sliver Visibility widget', + jsonAsset: 'assets/json/sliver_visibility_example.json', + dartAsset: 'lib/dsl/sliver_visibility.dart', + icon: 'app_fill', + iconType: 'cupertino', + category: EntryCategory.component, + ), + PlaygroundEntry( + id: 'opacity', + title: 'Stac Opacity', + description: 'A Material Design Stac Opacity widget', + jsonAsset: 'assets/json/opacity_example.json', + dartAsset: 'lib/dsl/opacity.dart', + icon: 'navigation', + iconType: 'material', + category: EntryCategory.component, + ), + PlaygroundEntry( + id: 'placeholder', + title: 'Stac Placeholder', + description: 'A Material Design Stac Placeholder widget', + jsonAsset: 'assets/json/placeholder_example.json', + dartAsset: 'lib/dsl/placeholder.dart', + icon: 'apartment', + iconType: 'material', + category: EntryCategory.component, + ), + PlaygroundEntry( + id: 'snackbar', + title: 'Stac SnackBar', + description: 'A Material Design SnackBar widget', + jsonAsset: 'assets/json/snackbar_example.json', + dartAsset: 'lib/dsl/snackbar.dart', + icon: 'message', + iconType: 'material', + category: EntryCategory.component, + ), + PlaygroundEntry( + id: 'aspect_ratio', + title: 'Stac AspectRatio', + description: 'A Material Design Stac AspectRatio widget', + jsonAsset: 'assets/json/aspect_ratio_example.json', + dartAsset: 'lib/dsl/aspect_ratio.dart', + icon: 'apartment', + iconType: 'material', + category: EntryCategory.component, + ), + PlaygroundEntry( + id: 'fitted_box', + title: 'Stac FittedBox', + description: 'A Material Design Stac FittedBox widget', + jsonAsset: 'assets/json/fitted_box_example.json', + dartAsset: 'lib/dsl/fitted_box.dart', + icon: 'apartment', + iconType: 'material', + category: EntryCategory.component, + ), + PlaygroundEntry( + id: 'limited_box', + title: 'Stac LimitedBox', + description: 'A Material Design Stac LimitedBox widget', + jsonAsset: 'assets/json/limited_box_example.json', + dartAsset: 'lib/dsl/limited_box.dart', + icon: 'apartment', + iconType: 'material', + category: EntryCategory.component, + ), + PlaygroundEntry( + id: 'dynamic_view', + title: 'Stac Dynamic View', + description: 'Fetch data from APIs and render it using templates', + jsonAsset: 'assets/json/dynamic_view_example.json', + dartAsset: 'lib/dsl/dynamic_view.dart', + icon: 'api', + iconType: 'material', + category: EntryCategory.component, + ), + PlaygroundEntry( + id: 'dynamic_list_view', + title: 'Stac Dynamic List View', + description: 'Fetch data from APIs and render it using templates', + jsonAsset: 'assets/json/dynamic_list_view_example.json', + dartAsset: 'lib/dsl/dynamic_list_view.dart', + icon: 'api', + iconType: 'material', + category: EntryCategory.component, + ), + PlaygroundEntry( + id: 'dropdown_menu_view', + title: 'Stac DropDown Menu', + description: 'Drop down menu to select items', + jsonAsset: 'assets/json/dropdown_menu_view_example.json', + dartAsset: 'lib/dsl/dropdown_menu_view.dart', + icon: 'api', + iconType: 'material', + category: EntryCategory.component, + ), + PlaygroundEntry( + id: 'variable', + title: 'Stac Variable', + description: 'Use variables to store and access values', + jsonAsset: 'assets/json/variable_example.json', + dartAsset: 'lib/dsl/variable.dart', + icon: 'category', + iconType: 'material', + category: EntryCategory.component, + ), + PlaygroundEntry( + id: 'conditional', + title: 'Conditional Rendering', + description: 'Use if-else conditions in STAC templates', + jsonAsset: 'assets/json/conditional_example.json', + dartAsset: 'lib/dsl/conditional.dart', + icon: 'code', + iconType: 'material', + category: EntryCategory.component, + ), + PlaygroundEntry( + id: 'backdrop_filter', + title: 'Backdrop Filter', + description: 'Use backdrop filters', + jsonAsset: 'assets/json/backdrop_filter_example.json', + dartAsset: 'lib/dsl/backdrop_filter.dart', + icon: 'code', + iconType: 'material', + category: EntryCategory.component, + ), +]; diff --git a/stac_playground/lib/data/dsl_to_json.dart b/stac_playground/lib/data/dsl_to_json.dart new file mode 100644 index 00000000..208747a6 --- /dev/null +++ b/stac_playground/lib/data/dsl_to_json.dart @@ -0,0 +1,800 @@ +/// Converts Stac DSL source text into the widget JSON the runtime renders. +/// +/// The real compiler (`stac build`) runs the analyzer, executes the annotated +/// `@StacScreen` function in a Dart process and calls `toJson()` on the result. +/// None of that is possible in an AOT-compiled web app, so this is a small +/// recursive-descent parser over the *declarative subset* of the DSL: a literal +/// tree of constructor calls, named arguments, literals, lists, maps and enum +/// references. That subset covers the playground's examples and maps to JSON +/// structurally, with no Dart evaluation. +/// +/// Calls to single-expression top-level helpers (`_socialRow(icon: …)`) are +/// inlined by binding the arguments and parsing the helper's returned +/// expression, since the examples lean on them heavily. +/// +/// What genuinely needs a Dart runtime — variables, conditionals, loops, +/// string interpolation, methods on non-Stac values — throws +/// [DslParseException] so callers can keep showing the last good preview. +library; + +import 'dart:convert'; + +/// Outcome of turning editor text into a preview tree. +/// +/// [json] is null when the text can't be converted; [message] is set only when +/// there's something worth telling the user (Dart outside the subset). A plain +/// mid-edit syntax error leaves both null so the preview just holds silently. +class EditorParseResult { + const EditorParseResult(this.json, this.message); + + final Map? json; + final String? message; +} + +/// Converts editor [text] to a widget tree — DSL source when [isDart], plain +/// JSON otherwise. Never throws; failures come back as an [EditorParseResult]. +EditorParseResult parseEditorSource(String text, {required bool isDart}) { + try { + final decoded = isDart ? dslToJson(text) : jsonDecode(text); + if (decoded is Map) { + return EditorParseResult(decoded, null); + } + return const EditorParseResult(null, null); + } on DslParseException catch (e) { + return EditorParseResult(null, e.message); + } catch (_) { + // Half-typed source; keep the last good preview without nagging. + return const EditorParseResult(null, null); + } +} + +/// Thrown when the source falls outside the declarative subset. +class DslParseException implements Exception { + const DslParseException(this.message); + + final String message; + + @override + String toString() => message; +} + +/// Widget classes (those extending `StacWidget`) — these serialize with a +/// `"type"` discriminator. Everything else (`StacTextStyle`, `StacBoxDecoration` +/// …) is a value object and serializes as a bare map. Generated from stac_core. +const Set _widgetClasses = { + 'StacAlertDialog', + 'StacAlign', + 'StacAppBar', + 'StacAspectRatio', + 'StacAutoComplete', + 'StacBackdropFilter', + 'StacBadge', + 'StacBottomNavigationBar', + 'StacBottomNavigationView', + 'StacCard', + 'StacCarouselView', + 'StacCenter', + 'StacCheckBox', + 'StacChip', + 'StacCircleAvatar', + 'StacCircularProgressIndicator', + 'StacClipOval', + 'StacClipRRect', + 'StacColoredBox', + 'StacColumn', + 'StacConditional', + 'StacContainer', + 'StacCustomScrollView', + 'StacDefaultBottomNavigationController', + 'StacDefaultNavigationController', + 'StacDefaultTabController', + 'StacDivider', + 'StacDrawer', + 'StacDropdownMenu', + 'StacDynamicView', + 'StacElevatedButton', + 'StacExpanded', + 'StacFilledButton', + 'StacFittedBox', + 'StacFlexible', + 'StacFloatingActionButton', + 'StacForm', + 'StacFractionallySizedBox', + 'StacGestureDetector', + 'StacGridView', + 'StacHero', + 'StacIcon', + 'StacIconButton', + 'StacImage', + 'StacInkWell', + 'StacLimitedBox', + 'StacLinearProgressIndicator', + 'StacListTile', + 'StacListView', + 'StacNavigationBar', + 'StacNavigationView', + 'StacNetworkWidget', + 'StacOpacity', + 'StacOutlinedButton', + 'StacPadding', + 'StacPageView', + 'StacPlaceholder', + 'StacPositioned', + 'StacRadio', + 'StacRadioGroup', + 'StacRefreshIndicator', + 'StacRow', + 'StacSafeArea', + 'StacScaffold', + 'StacSelectableText', + 'StacSetValue', + 'StacSingleChildScrollView', + 'StacSizedBox', + 'StacSlider', + 'StacSliverAppBar', + 'StacSliverFillRemaining', + 'StacSliverGrid', + 'StacSliverList', + 'StacSliverOpacity', + 'StacSliverPadding', + 'StacSliverSafeArea', + 'StacSliverToBoxAdapter', + 'StacSliverVisibility', + 'StacSpacer', + 'StacStack', + 'StacSwitch', + 'StacTab', + 'StacTabBar', + 'StacTabBarView', + 'StacTable', + 'StacTableCell', + 'StacText', + 'StacTextButton', + 'StacTextField', + 'StacTextFormField', + 'StacTooltip', + 'StacVerticalDivider', + 'StacVisibility', + 'StacWrap', + // From the stac_webview plugin rather than stac_core, but it is a StacWidget + // and serializes the same way. + 'StacWebView', +}; + +/// Classes whose `fromJson` expands a bare number into all four sides, so +/// `.all(8)` / `.circular(8)` can serialize as just `8`. +const Set _scalarExpandable = {'StacEdgeInsets', 'StacBorderRadius'}; + +/// The one class whose JSON type isn't the lower-camel form of its name. +const Map _typeOverrides = {'StacAutoComplete': 'autocomplete'}; + +String _typeForClass(String className) { + final override = _typeOverrides[className]; + if (override != null) return override; + final name = className.substring('Stac'.length); + return name[0].toLowerCase() + name.substring(1); +} + +/// A top-level helper function the screen can call, e.g. +/// `StacWidget _socialRow({required String icon}) { return StacRow(…); }`. +class _FunctionDef { + const _FunctionDef(this.positional, this.named, this.body); + + /// Parameter names, in declaration order. + final List positional; + final List named; + + /// Source of the single returned expression. + final String body; +} + +/// Parses [source] (a full DSL file) and returns the widget JSON tree. +Map dslToJson(String source) { + final stripped = _stripComments(source); + final expression = _returnExpressionOf(stripped); + final parser = _DslParser(expression, functions: _extractFunctions(stripped)); + final value = parser.parseValue(); + parser.skipTrivia(); + if (value is! Map) { + throw const DslParseException('The screen must return a Stac widget.'); + } + return value; +} + +/// Pulls the expression out of the first `return …;` in [block]. +String _returnExpressionOf(String block) { + final returnIndex = block.indexOf(RegExp(r'\breturn\b')); + if (returnIndex == -1) { + throw const DslParseException('No `return` found in the screen function.'); + } + final start = returnIndex + 'return'.length; + // Walk to the `;` that closes the return, ignoring ones nested in + // brackets or strings. + var depth = 0; + for (var i = start; i < block.length; i++) { + final ch = block[i]; + if (ch == "'" || ch == '"') { + i = _skipString(block, i); + continue; + } + if (ch == '(' || ch == '[' || ch == '{') depth++; + if (ch == ')' || ch == ']' || ch == '}') depth--; + if (ch == ';' && depth == 0) return block.substring(start, i); + } + throw const DslParseException('Unterminated `return` statement.'); +} + +/// Collects top-level functions so calls to them can be inlined. Only +/// single-expression helpers are usable; anything else is simply not recorded +/// and calling it reports that it needs evaluation. +Map _extractFunctions(String stripped) { + final defs = {}; + // Top-level declarations start at column 0 in formatted source: + // `StacWidget _socialRow({required String icon}) {` + final signature = RegExp( + r'^[A-Za-z_][A-Za-z0-9_<>,\s?]*?\s+([A-Za-z_][A-Za-z0-9_]*)\s*\(', + multiLine: true, + ); + for (final match in signature.allMatches(stripped)) { + final name = match.group(1)!; + final open = match.end - 1; + final close = _matchBracket(stripped, open, '(', ')'); + if (close == -1) continue; + final params = stripped.substring(open + 1, close); + + var i = close + 1; + while (i < stripped.length && stripped[i].trim().isEmpty) { + i++; + } + String? body; + if (i < stripped.length && stripped[i] == '{') { + final end = _matchBracket(stripped, i, '{', '}'); + if (end == -1) continue; + try { + body = _returnExpressionOf(stripped.substring(i + 1, end)); + } catch (_) { + continue; // not a single-return helper + } + } else if (stripped.startsWith('=>', i)) { + var depth = 0; + for (var j = i + 2; j < stripped.length; j++) { + final ch = stripped[j]; + if (ch == "'" || ch == '"') { + j = _skipString(stripped, j); + continue; + } + if (ch == '(' || ch == '[' || ch == '{') depth++; + if (ch == ')' || ch == ']' || ch == '}') depth--; + if (ch == ';' && depth == 0) { + body = stripped.substring(i + 2, j); + break; + } + } + } + if (body == null) continue; + final parsed = _parseParameterNames(params); + defs[name] = _FunctionDef(parsed.$1, parsed.$2, body); + } + return defs; +} + +/// Returns (positional, named) parameter names from a parameter list source. +(List, List) _parseParameterNames(String params) { + final positional = []; + final named = []; + final braceStart = params.indexOf('{'); + final positionalSrc = + braceStart == -1 ? params : params.substring(0, braceStart); + final namedSrc = braceStart == -1 + ? '' + : params.substring( + braceStart + 1, + params.lastIndexOf('}') == -1 + ? params.length + : params.lastIndexOf('}')); + + void collect(String src, List into) { + for (final part in src.split(',')) { + final trimmed = part.trim(); + if (trimmed.isEmpty) continue; + // The parameter name is the final identifier: `required String icon`. + final ident = RegExp(r'([A-Za-z_][A-Za-z0-9_]*)\s*$').firstMatch(trimmed); + if (ident != null) into.add(ident.group(1)!); + } + } + + collect(positionalSrc, positional); + collect(namedSrc, named); + return (positional, named); +} + +/// Index of the bracket closing the one at [start], or -1. +int _matchBracket(String s, int start, String open, String close) { + var depth = 0; + for (var i = start; i < s.length; i++) { + final ch = s[i]; + if (ch == "'" || ch == '"') { + i = _skipString(s, i); + continue; + } + if (ch == open) depth++; + if (ch == close) { + depth--; + if (depth == 0) return i; + } + } + return -1; +} + +/// Returns the index of the closing quote of the string starting at [start]. +int _skipString(String s, int start) { + final quote = s[start]; + for (var i = start + 1; i < s.length; i++) { + if (s[i] == r'\') { + i++; + continue; + } + if (s[i] == quote) return i; + } + throw const DslParseException('Unterminated string literal.'); +} + +/// Removes `//` and `/* */` comments, preserving anything inside strings. +String _stripComments(String s) { + final out = StringBuffer(); + for (var i = 0; i < s.length; i++) { + final ch = s[i]; + if (ch == "'" || ch == '"') { + final end = _skipString(s, i); + out.write(s.substring(i, end + 1)); + i = end; + continue; + } + if (ch == '/' && i + 1 < s.length) { + if (s[i + 1] == '/') { + while (i < s.length && s[i] != '\n') { + i++; + } + out.write('\n'); + continue; + } + if (s[i + 1] == '*') { + final end = s.indexOf('*/', i + 2); + i = end == -1 ? s.length : end + 1; + out.write(' '); + continue; + } + } + out.write(ch); + } + return out.toString(); +} + +class _DslParser { + _DslParser( + this.src, { + this.functions = const {}, + this.bindings = const {}, + this.depth = 0, + }); + + final String src; + + /// Top-level helpers available to inline. + final Map functions; + + /// Parameter values bound while inlining a helper body. + final Map bindings; + + /// Guards against helpers that call themselves. + final int depth; + + int pos = 0; + + bool get _atEnd => pos >= src.length; + + void skipTrivia() { + while (!_atEnd && src[pos].trim().isEmpty) { + pos++; + } + } + + bool _consume(String token) { + skipTrivia(); + if (src.startsWith(token, pos)) { + pos += token.length; + return true; + } + return false; + } + + void _expect(String token) { + if (!_consume(token)) { + throw DslParseException("Expected '$token' near: ${_context()}"); + } + } + + String _context() { + final end = (pos + 30).clamp(0, src.length); + return src.substring(pos.clamp(0, src.length), end).trim(); + } + + /// Parses any DSL value: literal, list, map, enum reference or constructor. + dynamic parseValue() { + skipTrivia(); + if (_atEnd) throw const DslParseException('Unexpected end of expression.'); + final ch = src[pos]; + + // Explicit type arguments on a collection literal: `>[…]`. + if (ch == '<') { + _skipTypeArguments(); + return parseValue(); + } + + if (ch == "'" || ch == '"') return _parseStringLiteral(); + if (ch == '[') return _parseList(); + if (ch == '{') return _parseMap(); + if (ch == '-' || _isDigit(ch)) return _parseNumber(); + + if (src.startsWith('r', pos) && + pos + 1 < src.length && + (src[pos + 1] == "'" || src[pos + 1] == '"')) { + return _parseStringLiteral(); + } + + if (_matchKeyword('true')) return true; + if (_matchKeyword('false')) return false; + if (_matchKeyword('null')) return null; + if (_matchKeyword('const') || _matchKeyword('new')) return parseValue(); + + if (_isIdentifierStart(ch)) return _parseIdentifierExpression(); + + throw DslParseException('Unsupported expression near: ${_context()}'); + } + + /// Skips a balanced `<…>` type-argument list. + void _skipTypeArguments() { + skipTrivia(); + if (_atEnd || src[pos] != '<') return; + var depth = 0; + while (!_atEnd) { + if (src[pos] == '<') depth++; + if (src[pos] == '>') { + depth--; + if (depth == 0) { + pos++; + return; + } + } + pos++; + } + throw const DslParseException('Unterminated type arguments.'); + } + + /// Matches a bare keyword, making sure it isn't the prefix of a longer + /// identifier (`nullable`, `constant`, …). + bool _matchKeyword(String word) { + skipTrivia(); + if (!src.startsWith(word, pos)) return false; + final after = pos + word.length; + if (after < src.length && _isIdentifierPart(src[after])) return false; + pos = after; + return true; + } + + bool _isDigit(String c) => c.compareTo('0') >= 0 && c.compareTo('9') <= 0; + + bool _isIdentifierStart(String c) => + c == '_' || + (c.toLowerCase() != c.toUpperCase()) || + (c.compareTo('a') >= 0 && c.compareTo('z') <= 0); + + bool _isIdentifierPart(String c) => _isIdentifierStart(c) || _isDigit(c); + + String _readIdentifier() { + skipTrivia(); + final start = pos; + while (!_atEnd && _isIdentifierPart(src[pos])) { + pos++; + } + if (start == pos) { + throw DslParseException('Expected an identifier near: ${_context()}'); + } + return src.substring(start, pos); + } + + /// Dart concatenates adjacent string literals, and `dart format` splits long + /// URLs that way, so keep reading them. + String _parseStringLiteral() { + final buffer = StringBuffer(); + while (true) { + skipTrivia(); + if (_atEnd) break; + var isRaw = false; + if (src[pos] == 'r' && + pos + 1 < src.length && + (src[pos + 1] == "'" || src[pos + 1] == '"')) { + isRaw = true; + pos++; + } + if (src[pos] != "'" && src[pos] != '"') break; + final quote = src[pos]; + // Triple-quoted strings. + final triple = src.startsWith(quote * 3, pos); + if (triple) { + final close = src.indexOf(quote * 3, pos + 3); + if (close == -1) { + throw const DslParseException('Unterminated string literal.'); + } + buffer.write(src.substring(pos + 3, close)); + pos = close + 3; + continue; + } + pos++; + while (!_atEnd && src[pos] != quote) { + if (!isRaw && src[pos] == r'\') { + pos++; + if (_atEnd) break; + buffer.write(_unescape(src[pos])); + pos++; + continue; + } + // `$` is literal inside a raw string, so only flag interpolation for + // ordinary literals. + if (!isRaw && src[pos] == r'$') { + throw const DslParseException( + 'String interpolation needs evaluation and cannot be previewed.', + ); + } + buffer.write(src[pos]); + pos++; + } + if (_atEnd) throw const DslParseException('Unterminated string literal.'); + pos++; // closing quote + // Peek for an adjacent literal to concatenate. + final save = pos; + skipTrivia(); + final adjacent = !_atEnd && + (src[pos] == "'" || + src[pos] == '"' || + (src[pos] == 'r' && + pos + 1 < src.length && + (src[pos + 1] == "'" || src[pos + 1] == '"'))); + if (!adjacent) { + pos = save; + break; + } + } + return buffer.toString(); + } + + String _unescape(String c) => switch (c) { + 'n' => '\n', + 't' => '\t', + 'r' => '\r', + 'b' => '\b', + _ => c, + }; + + num _parseNumber() { + skipTrivia(); + final start = pos; + if (!_atEnd && src[pos] == '-') pos++; + while (!_atEnd && (_isDigit(src[pos]) || src[pos] == '.')) { + pos++; + } + final text = src.substring(start, pos); + final value = num.tryParse(text); + if (value == null) throw DslParseException('Invalid number: $text'); + return value; + } + + List _parseList() { + _expect('['); + final items = []; + while (true) { + skipTrivia(); + if (_consume(']')) break; + items.add(parseValue()); + skipTrivia(); + if (_consume(',')) continue; + _expect(']'); + break; + } + return items; + } + + Map _parseMap() { + _expect('{'); + final map = {}; + while (true) { + skipTrivia(); + if (_consume('}')) break; + final key = parseValue(); + if (key is! String) { + throw const DslParseException('Map keys must be string literals.'); + } + _expect(':'); + map[key] = parseValue(); + skipTrivia(); + if (_consume(',')) continue; + _expect('}'); + break; + } + return map; + } + + /// Handles `StacText(...)`, `StacEdgeInsets.only(...)`, `Enum.value`, and a + /// trailing `.toJson()`. + dynamic _parseIdentifierExpression() { + final name = _readIdentifier(); + + String? memberName; + final save = pos; + skipTrivia(); + if (_consume('.')) { + memberName = _readIdentifier(); + } else { + pos = save; + } + + skipTrivia(); + final hasArgs = !_atEnd && src[pos] == '('; + if (!hasArgs) { + if (memberName == null) { + // A helper parameter bound while inlining, e.g. `icon` inside + // `_socialRow`'s body. + if (bindings.containsKey(name)) return bindings[name]; + throw DslParseException( + "'$name' needs evaluation and cannot be previewed.", + ); + } + // Enum or static constant: `StacFontWeight.w600`, `double.maxFinite`. + return memberName; + } + + final helper = memberName == null ? functions[name] : null; + if (helper != null) { + final args = _parseArguments(); + _consumeTrailingToJson(); + return _inlineHelper(name, helper, args); + } + + if (!name.startsWith('Stac')) { + throw DslParseException( + "'$name(...)' needs evaluation and cannot be previewed.", + ); + } + + final args = _parseArguments(); + _consumeTrailingToJson(); + return _buildJson(name, memberName, args); + } + + /// Inlines a single-expression helper by binding its parameters to the call + /// arguments and parsing its body in that scope. + dynamic _inlineHelper(String name, _FunctionDef def, _Arguments args) { + if (depth > 8) { + throw DslParseException("'$name(...)' recurses too deeply to preview."); + } + final scope = {}; + for (var i = 0; + i < def.positional.length && i < args.positional.length; + i++) { + scope[def.positional[i]] = args.positional[i]; + } + for (final param in def.named) { + if (args.named.containsKey(param)) scope[param] = args.named[param]; + } + final inner = _DslParser( + def.body, + functions: functions, + bindings: scope, + depth: depth + 1, + ); + final value = inner.parseValue(); + return value; + } + + void _consumeTrailingToJson() { + final save = pos; + skipTrivia(); + if (_consume('.')) { + skipTrivia(); + if (src.startsWith('toJson', pos)) { + pos += 'toJson'.length; + _expect('('); + _expect(')'); + return; + } + } + pos = save; + } + + _Arguments _parseArguments() { + _expect('('); + final positional = []; + final named = {}; + while (true) { + skipTrivia(); + if (_consume(')')) break; + // Named argument? `name:` — but not `Enum.value` or a string key. + final save = pos; + var isNamed = false; + String? label; + if (!_atEnd && _isIdentifierStart(src[pos])) { + final ident = _readIdentifier(); + skipTrivia(); + if (!_atEnd && src[pos] == ':') { + pos++; + isNamed = true; + label = ident; + } else { + pos = save; + } + } + final value = parseValue(); + if (isNamed) { + named[label!] = value; + } else { + positional.add(value); + } + skipTrivia(); + if (_consume(',')) continue; + _expect(')'); + break; + } + return _Arguments(positional, named); + } + + dynamic _buildJson(String className, String? constructor, _Arguments args) { + // `StacEdgeInsets.all(8)` / `StacBorderRadius.circular(8)` → 8, which + // fromJson expands across all sides. + if (_scalarExpandable.contains(className) && + (constructor == 'all' || constructor == 'circular') && + args.positional.length == 1 && + args.named.isEmpty) { + return args.positional.first; + } + + // `StacEdgeInsets.symmetric(horizontal: h, vertical: v)`. + if (className == 'StacEdgeInsets' && constructor == 'symmetric') { + final horizontal = args.named['horizontal']; + final vertical = args.named['vertical']; + return { + if (horizontal != null) 'left': horizontal, + if (horizontal != null) 'right': horizontal, + if (vertical != null) 'top': vertical, + if (vertical != null) 'bottom': vertical, + }; + } + + // `StacAction(jsonData: {...})` is the escape hatch for a raw action map, + // and it serializes as that map rather than as a wrapper around it. + if (className == 'StacAction') { + final raw = args.named['jsonData']; + return raw is Map ? raw : {}; + } + + if (args.positional.isNotEmpty) { + throw DslParseException( + '$className${constructor == null ? '' : '.$constructor'}() uses ' + 'positional arguments that cannot be mapped to JSON.', + ); + } + + final json = {}; + if (_widgetClasses.contains(className)) { + json['type'] = _typeForClass(className); + } + json.addAll(args.named); + return json; + } +} + +class _Arguments { + const _Arguments(this.positional, this.named); + + final List positional; + final Map named; +} diff --git a/stac_playground/lib/data/playground_entry.dart b/stac_playground/lib/data/playground_entry.dart new file mode 100644 index 00000000..109e7090 --- /dev/null +++ b/stac_playground/lib/data/playground_entry.dart @@ -0,0 +1,32 @@ +enum EntryCategory { screen, component } + +/// One item in the playground index: a full screen sample or a widget +/// component example. Content comes either inline ([json]/[dartCode]) or +/// from bundled assets ([jsonAsset]/[dartAsset]). +class PlaygroundEntry { + const PlaygroundEntry({ + required this.id, + required this.title, + this.description = '', + this.category = EntryCategory.screen, + this.json, + this.dartCode, + this.jsonAsset, + this.dartAsset, + this.icon, + this.iconType = 'material', + }); + + final String id; + final String title; + final String description; + final EntryCategory category; + final Map? json; + final String? dartCode; + final String? jsonAsset; + final String? dartAsset; + + /// Icon name rendered through Stac's icon parser (material/cupertino set). + final String? icon; + final String iconType; +} diff --git a/stac_playground/lib/dsl/align.dart b/stac_playground/lib/dsl/align.dart new file mode 100644 index 00000000..907fddc8 --- /dev/null +++ b/stac_playground/lib/dsl/align.dart @@ -0,0 +1,19 @@ +import 'package:stac_core/stac_core.dart'; + +@StacScreen(screenName: 'align') +StacWidget alignExample() { + return StacScaffold( + appBar: StacAppBar(title: StacText(data: 'Align')), + body: StacAlign( + alignment: StacAlignmentDirectional.topEnd, + child: StacContainer( + color: '#FC5632', + height: 250, + width: 200, + child: StacAlign( + alignment: StacAlignmentDirectional.bottomCenter, + child: StacText( + data: 'Flutter', + style: StacTextStyle( + fontSize: 23, fontWeight: StacFontWeight.w600)))))); +} diff --git a/stac_playground/lib/dsl/app_bar.dart b/stac_playground/lib/dsl/app_bar.dart new file mode 100644 index 00000000..899ecd1c --- /dev/null +++ b/stac_playground/lib/dsl/app_bar.dart @@ -0,0 +1,17 @@ +import 'package:stac_core/stac_core.dart'; + +@StacScreen(screenName: 'app_bar') +StacWidget appBarExample() { + return StacScaffold( + appBar: StacAppBar( + automaticallyImplyLeading: true, + title: StacText(data: 'Stac Appbar'), + primary: true, + excludeHeaderSemantics: false, + toolbarOpacity: 1, + bottomOpacity: 1, + forceMaterialTransparency: false, + useDefaultSemanticsOrder: true), + body: StacCenter( + child: StacText(data: 'Home', style: StacTextStyle(fontSize: 17)))); +} diff --git a/stac_playground/lib/dsl/aspect_ratio.dart b/stac_playground/lib/dsl/aspect_ratio.dart new file mode 100644 index 00000000..791c9b85 --- /dev/null +++ b/stac_playground/lib/dsl/aspect_ratio.dart @@ -0,0 +1,19 @@ +import 'package:stac_core/stac_core.dart'; + +@StacScreen(screenName: 'aspect_ratio') +StacWidget aspectRatioExample() { + return StacScaffold( + appBar: StacAppBar(title: StacText(data: 'AspectRatio')), + body: StacPadding( + padding: StacEdgeInsets.only(top: 12, left: 12, right: 12), + child: StacColumn( + mainAxisAlignment: StacMainAxisAlignment.start, + crossAxisAlignment: StacCrossAxisAlignment.start, + children: [ + StacSizedBox(height: 12), + StacAspectRatio( + aspectRatio: 1.33, + child: StacContainer( + color: '#FF5733', width: 100, height: 100)) + ]))); +} diff --git a/stac_playground/lib/dsl/auto_complete.dart b/stac_playground/lib/dsl/auto_complete.dart new file mode 100644 index 00000000..6596d90b --- /dev/null +++ b/stac_playground/lib/dsl/auto_complete.dart @@ -0,0 +1,16 @@ +import 'package:stac_core/stac_core.dart'; + +@StacScreen(screenName: 'auto_complete') +StacWidget autoCompleteExample() { + return StacScaffold( + appBar: StacAppBar(title: StacText(data: 'Autocomplete Basic')), + body: StacCenter( + child: StacColumn(children: [ + StacText( + data: + 'Type below to autocomplete the following possible results: [aardvark, bobcat, chameleon].'), + StacAutoComplete( + options: ['aardvark', 'bobcat', 'chameleon'], + onSelected: StacAction()) + ]))); +} diff --git a/stac_playground/lib/dsl/backdrop_filter.dart b/stac_playground/lib/dsl/backdrop_filter.dart new file mode 100644 index 00000000..fdd38005 --- /dev/null +++ b/stac_playground/lib/dsl/backdrop_filter.dart @@ -0,0 +1,270 @@ +import 'package:stac_core/stac_core.dart'; + +@StacScreen(screenName: 'backdrop_filter') +StacWidget backdropFilterExample() { + return StacScaffold( + appBar: StacAppBar(title: StacText(data: 'Backdrop Filter Example')), + body: StacSingleChildScrollView( + child: StacColumn( + mainAxisAlignment: StacMainAxisAlignment.center, + crossAxisAlignment: StacCrossAxisAlignment.center, + children: [ + StacPadding( + padding: StacEdgeInsets.all(16), + child: StacText( + data: + 'Backdrop filters apply visual effects to everything behind a widget.', + textAlign: StacTextAlign.center, + style: + StacTextStyle(fontSize: 16, fontWeight: StacFontWeight.w700), + ), + ), + StacDivider(height: 20), + StacPadding( + padding: StacEdgeInsets.all(16), + child: StacText( + data: 'Blur Filter with sigmaX: 10.0, sigmaY: 10.0', + style: StacTextStyle(fontSize: 18), + ), + ), + StacClipRRect( + borderRadius: StacBorderRadius.all(16), + child: StacSizedBox( + height: 200, + width: 300, + child: StacStack( + fit: StacStackFit.expand, + children: [ + StacContainer( + decoration: StacBoxDecoration( + image: StacDecorationImage( + src: + 'https://images.pexels.com/photos/1323550/pexels-photo-1323550.jpeg', + fit: StacBoxFit.cover, + ), + ), + ), + StacBackdropFilter( + filter: StacImageFilter( + type: StacImageFilterType.blur, + sigmaX: 10.0, + sigmaY: 10.0, + ), + child: StacContainer( + decoration: StacBoxDecoration(color: '#80FFFFFF'), + ), + ), + ], + ), + ), + ), + StacDivider(height: 20), + StacPadding( + padding: StacEdgeInsets.all(16), + child: StacText( + data: 'Dilate Filter with radiusX: 2.0, radiusY: 2.0', + style: StacTextStyle(fontSize: 18), + ), + ), + StacClipRRect( + borderRadius: StacBorderRadius.all(16), + child: StacSizedBox( + height: 200, + width: 300, + child: StacStack( + fit: StacStackFit.expand, + children: [ + StacContainer( + decoration: StacBoxDecoration( + image: StacDecorationImage( + src: + 'https://images.pexels.com/photos/1323550/pexels-photo-1323550.jpeg', + fit: StacBoxFit.cover, + ), + ), + ), + StacBackdropFilter( + filter: StacImageFilter( + type: StacImageFilterType.dilate, + radiusX: 2.0, + radiusY: 2.0, + ), + child: StacContainer( + decoration: StacBoxDecoration(color: '#80FFFFFF'), + ), + ), + ], + ), + ), + ), + StacDivider(height: 20), + StacPadding( + padding: StacEdgeInsets.all(16), + child: StacText( + data: 'Erode Filter with radiusX: 2.0, radiusY: 2.0', + style: StacTextStyle(fontSize: 18), + ), + ), + StacClipRRect( + borderRadius: StacBorderRadius.all(16), + child: StacSizedBox( + height: 200, + width: 300, + child: StacStack( + fit: StacStackFit.expand, + children: [ + StacContainer( + decoration: StacBoxDecoration( + image: StacDecorationImage( + src: + 'https://images.pexels.com/photos/1323550/pexels-photo-1323550.jpeg', + fit: StacBoxFit.cover, + ), + ), + ), + StacBackdropFilter( + filter: StacImageFilter( + type: StacImageFilterType.erode, + radiusX: 2.0, + radiusY: 2.0, + ), + child: StacContainer( + decoration: StacBoxDecoration(color: '#80FFFFFF'), + ), + ), + ], + ), + ), + ), + StacDivider(height: 20), + StacPadding( + padding: StacEdgeInsets.all(16), + child: StacText( + data: 'Frosted Glass Effect with sigmaX: 15.0, sigmaY: 15.0', + style: StacTextStyle(fontSize: 18), + ), + ), + StacClipRRect( + borderRadius: StacBorderRadius.all(16), + child: StacSizedBox( + height: 200, + width: 300, + child: StacStack( + fit: StacStackFit.expand, + children: [ + StacContainer( + decoration: StacBoxDecoration( + image: StacDecorationImage( + src: + 'https://images.pexels.com/photos/1323550/pexels-photo-1323550.jpeg', + fit: StacBoxFit.cover, + ), + ), + ), + StacBackdropFilter( + filter: StacImageFilter( + type: StacImageFilterType.blur, + sigmaX: 15.0, + sigmaY: 15.0, + ), + child: StacContainer( + decoration: StacBoxDecoration(color: '#80FFFFFF'), + ), + ), + ], + ), + ), + ), + StacDivider(height: 20), + StacPadding( + padding: StacEdgeInsets.all(16), + child: StacText( + data: 'Compose Filter (Blur + Dilate)', + style: StacTextStyle(fontSize: 18), + ), + ), + StacClipRRect( + borderRadius: StacBorderRadius.all(16), + child: StacSizedBox( + height: 200, + width: 300, + child: StacStack( + fit: StacStackFit.expand, + children: [ + StacContainer( + decoration: StacBoxDecoration( + image: StacDecorationImage( + src: + 'https://images.pexels.com/photos/1323550/pexels-photo-1323550.jpeg', + fit: StacBoxFit.cover, + ), + ), + ), + StacBackdropFilter( + filter: StacImageFilter( + type: StacImageFilterType.compose, + outer: StacImageFilter( + type: StacImageFilterType.blur, + sigmaX: 5.0, + sigmaY: 5.0, + ), + inner: StacImageFilter( + type: StacImageFilterType.dilate, + radiusX: 2.0, + radiusY: 2.0, + ), + ), + child: StacContainer( + decoration: StacBoxDecoration(color: '#80FFFFFF'), + ), + ), + ], + ), + ), + ), + StacDivider(height: 20), + StacPadding( + padding: StacEdgeInsets.all(16), + child: StacText( + data: 'Blur Filter with BlendMode dstOver', + style: StacTextStyle(fontSize: 18), + ), + ), + StacClipRRect( + borderRadius: StacBorderRadius.all(16), + child: StacSizedBox( + height: 200, + width: 300, + child: StacStack( + fit: StacStackFit.expand, + children: [ + StacContainer( + decoration: StacBoxDecoration( + image: StacDecorationImage( + src: + 'https://images.pexels.com/photos/1323550/pexels-photo-1323550.jpeg', + fit: StacBoxFit.cover, + ), + ), + ), + StacBackdropFilter( + filter: StacImageFilter( + type: StacImageFilterType.blur, + radiusX: 10.0, + radiusY: 10.0, + ), + blendMode: StacBlendMode.dstOver, + child: StacContainer( + decoration: StacBoxDecoration(color: '#80FFFFFF'), + ), + ), + ], + ), + ), + ), + StacDivider(height: 20), + ], + ), + ), + ); +} diff --git a/stac_playground/lib/dsl/badge.dart b/stac_playground/lib/dsl/badge.dart new file mode 100644 index 00000000..c14c6127 --- /dev/null +++ b/stac_playground/lib/dsl/badge.dart @@ -0,0 +1,87 @@ +import 'package:stac_core/stac_core.dart'; + +@StacScreen(screenName: 'badge') +StacWidget badgeExample() { + return StacScaffold( + appBar: StacAppBar(title: StacText(data: 'Badge')), + body: StacColumn( + mainAxisAlignment: StacMainAxisAlignment.center, + crossAxisAlignment: StacCrossAxisAlignment.center, + children: [ + StacText( + data: 'Badge with Label', + style: StacTextStyle( + fontSize: 18, fontWeight: StacFontWeight.bold)), + StacSizedBox(height: 16), + StacRow( + mainAxisAlignment: StacMainAxisAlignment.center, + crossAxisAlignment: StacCrossAxisAlignment.center, + children: [ + StacBadge( + label: StacText(data: '5'), + child: StacIcon(icon: 'notifications', size: 32)), + StacSizedBox(width: 24), + StacBadge( + label: StacText(data: 'NEW'), + backgroundColor: '#4CAF50', + textColor: '#FFFFFF', + child: StacIcon(icon: 'mail', size: 32)) + ]), + StacSizedBox(height: 32), + StacText( + data: 'Badge with Count', + style: StacTextStyle( + fontSize: 18, fontWeight: StacFontWeight.bold)), + StacSizedBox(height: 16), + StacRow( + mainAxisAlignment: StacMainAxisAlignment.center, + crossAxisAlignment: StacCrossAxisAlignment.center, + children: [ + StacBadge( + count: 5, + child: StacIcon(icon: 'shopping_cart', size: 32)), + StacSizedBox(width: 24), + StacBadge( + count: 99, + maxCount: 99, + child: StacIcon(icon: 'favorite', size: 32)), + StacSizedBox(width: 24), + StacBadge( + count: 1000, + maxCount: 99, + child: StacIcon(icon: 'notifications', size: 32)) + ]), + StacSizedBox(height: 32), + StacText( + data: 'Small Badge (No Label)', + style: StacTextStyle( + fontSize: 18, fontWeight: StacFontWeight.bold)), + StacSizedBox(height: 16), + StacRow( + mainAxisAlignment: StacMainAxisAlignment.center, + crossAxisAlignment: StacCrossAxisAlignment.center, + children: [ + StacBadge( + smallSize: 8, + backgroundColor: '#F44336', + child: StacIcon(icon: 'circle', size: 32)), + StacSizedBox(width: 24), + StacBadge( + smallSize: 12, + backgroundColor: '#4CAF50', + child: StacIcon(icon: 'check_circle', size: 32)) + ]), + StacSizedBox(height: 32), + StacText( + data: 'Badge on IconButton', + style: StacTextStyle( + fontSize: 18, fontWeight: StacFontWeight.bold)), + StacSizedBox(height: 16), + StacBadge( + count: 3, + child: StacIconButton( + icon: StacIcon(icon: 'notifications', size: 24), + padding: StacEdgeInsets.only( + left: 0, top: 0, right: 0, bottom: 0))) + ])); +} diff --git a/stac_playground/lib/dsl/bottom_nav_bar.dart b/stac_playground/lib/dsl/bottom_nav_bar.dart new file mode 100644 index 00000000..330cc25b --- /dev/null +++ b/stac_playground/lib/dsl/bottom_nav_bar.dart @@ -0,0 +1,49 @@ +// The JSON for this example uses the deprecated `defaultBottomNavigationController` +// and `bottomNavigationView` types, so the DSL mirrors them to stay faithful. +// The navigation_bar example demonstrates the replacements. +// ignore_for_file: deprecated_member_use + +import 'package:stac_core/stac_core.dart'; + +@StacScreen(screenName: 'bottom_nav_bar') +StacWidget bottomNavBarExample() { + return StacDefaultBottomNavigationController( + length: 3, + child: StacScaffold( + appBar: StacAppBar(title: StacText(data: 'Bottom Navigation Screen')), + body: StacBottomNavigationView( + children: [ + StacCenter( + child: StacText(data: 'Home', style: StacTextStyle(fontSize: 24)), + ), + StacCenter( + child: StacText(data: 'Search', style: StacTextStyle(fontSize: 24)), + ), + StacCenter( + child: + StacText(data: 'Profile', style: StacTextStyle(fontSize: 24)), + ), + ], + ), + bottomNavigationBar: StacBottomNavigationBar( + items: [ + StacBottomNavigationBarItem( + label: 'Home', + icon: StacIcon(iconType: StacIconType.material, icon: 'home'), + ), + StacBottomNavigationBarItem( + label: 'Search', + icon: StacIcon(iconType: StacIconType.material, icon: 'search'), + ), + StacBottomNavigationBarItem( + label: 'Profile', + icon: StacIcon( + iconType: StacIconType.material, + icon: 'account_circle', + ), + ), + ], + ), + ), + ); +} diff --git a/stac_playground/lib/dsl/bottom_sheet.dart b/stac_playground/lib/dsl/bottom_sheet.dart new file mode 100644 index 00000000..03412e08 --- /dev/null +++ b/stac_playground/lib/dsl/bottom_sheet.dart @@ -0,0 +1,73 @@ +import 'package:stac_core/stac_core.dart'; + +@StacScreen(screenName: 'bottom_sheet') +StacWidget bottomSheetExample() { + return StacScaffold( + appBar: StacAppBar(title: StacText(data: 'BottomSheet')), + body: StacRow( + mainAxisAlignment: StacMainAxisAlignment.center, + crossAxisAlignment: StacCrossAxisAlignment.center, + children: [ + StacColumn( + mainAxisAlignment: StacMainAxisAlignment.center, + crossAxisAlignment: StacCrossAxisAlignment.center, + children: [ + StacElevatedButton( + style: StacButtonStyle( + padding: StacEdgeInsets.only( + top: 8, + left: 12, + right: 12, + bottom: 8, + ), + ), + onPressed: StacModalBottomSheetAction( + widget: StacContainer( + height: 200, + padding: StacEdgeInsets.only( + top: 16, + left: 16, + right: 16, + bottom: 16, + ), + child: StacColumn( + children: [ + StacRow( + mainAxisAlignment: StacMainAxisAlignment.spaceBetween, + children: [ + StacText( + data: 'Modal Bottom Sheet', + style: StacTextStyle( + fontSize: 18, + fontWeight: StacFontWeight.bold, + ), + ), + StacIconButton( + icon: StacIcon( + iconType: StacIconType.material, + icon: 'close', + ), + onPressed: StacNavigateAction( + navigationStyle: NavigationStyle.pop, + ), + ), + ], + ), + StacPadding( + padding: StacEdgeInsets.only(top: 16), + child: StacText( + data: 'This is a simple modal bottom sheet example.', + ), + ), + ], + ), + ), + ), + child: StacText(data: 'Modal Bottom Sheet'), + ), + ], + ), + ], + ), + ); +} diff --git a/stac_playground/lib/dsl/card.dart b/stac_playground/lib/dsl/card.dart new file mode 100644 index 00000000..52625c02 --- /dev/null +++ b/stac_playground/lib/dsl/card.dart @@ -0,0 +1,43 @@ +import 'package:stac_core/stac_core.dart'; + +@StacScreen(screenName: 'card') +StacWidget cardExample() { + return StacScaffold( + appBar: StacAppBar(title: StacText(data: 'Cards')), + body: StacColumn( + mainAxisAlignment: StacMainAxisAlignment.start, + crossAxisAlignment: StacCrossAxisAlignment.center, + children: [ + StacSizedBox(height: 12), + StacCard( + elevation: 20, + borderOnForeground: true, + margin: StacEdgeInsets.only(top: 20, bottom: 20, right: 20, left: 20), + child: StacListTile( + leading: StacImage( + src: + 'https://images.pexels.com/photos/220453/pexels-photo-220453.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=2', + width: 50, + height: 50, + ), + title: StacPadding( + padding: StacEdgeInsets.only(top: 10), + child: StacText( + data: 'Prof. Richard Jhonson', + style: StacTextStyle(fontSize: 21), + ), + ), + subtitle: StacPadding( + padding: StacEdgeInsets.only(top: 10, bottom: 10), + child: StacText( + data: + 'Head of Department of Computer Science, The New York University Campus, Abu Dhabi, United Arab Emirates.', + style: StacTextStyle(fontSize: 12), + ), + ), + ), + ), + ], + ), + ); +} diff --git a/stac_playground/lib/dsl/carousel_view.dart b/stac_playground/lib/dsl/carousel_view.dart new file mode 100644 index 00000000..a7dd7795 --- /dev/null +++ b/stac_playground/lib/dsl/carousel_view.dart @@ -0,0 +1,108 @@ +import 'package:stac_core/stac_core.dart'; + +@StacScreen(screenName: 'carousel_view') +StacWidget carouselViewExample() { + return StacScaffold( + appBar: StacAppBar(title: StacText(data: 'Carousel View')), + body: StacListView( + children: [ + StacContainer( + height: 400, + child: StacCarouselView( + padding: StacEdgeInsets.all(12), + carouselType: StacCarouselViewType.weighted, + itemSnapping: true, + flexWeights: [1, 7, 1], + children: [ + StacImage( + height: 400, + fit: StacBoxFit.cover, + src: + 'https://flutter.github.io/assets-for-api-docs/assets/material/content_based_color_scheme_1.png', + ), + StacImage( + height: 400, + fit: StacBoxFit.cover, + src: + 'https://flutter.github.io/assets-for-api-docs/assets/material/content_based_color_scheme_2.png', + ), + StacImage( + height: 400, + fit: StacBoxFit.cover, + src: + 'https://flutter.github.io/assets-for-api-docs/assets/material/content_based_color_scheme_3.png', + ), + StacImage( + height: 400, + fit: StacBoxFit.cover, + src: + 'https://flutter.github.io/assets-for-api-docs/assets/material/content_based_color_scheme_4.png', + ), + StacImage( + height: 400, + fit: StacBoxFit.cover, + src: + 'https://flutter.github.io/assets-for-api-docs/assets/material/content_based_color_scheme_5.png', + ), + StacImage( + height: 400, + fit: StacBoxFit.cover, + src: + 'https://flutter.github.io/assets-for-api-docs/assets/material/content_based_color_scheme_6.png', + ), + ], + ), + ), + StacContainer( + height: 200, + child: StacCarouselView( + itemExtent: 300, + shrinkExtent: 80, + padding: StacEdgeInsets.all(12), + children: [ + StacContainer( + color: '#FFCDD2', + child: StacCenter( + child: StacText( + data: 'Show 0', + style: StacTextStyle( + color: '#FFFFFF', + fontSize: 20, + fontWeight: StacFontWeight.w400, + ), + ), + ), + ), + StacContainer( + color: '#C8E6C9', + child: StacCenter( + child: StacText( + data: 'Show 1', + style: StacTextStyle( + color: '#FFFFFF', + fontSize: 20, + fontWeight: StacFontWeight.w400, + ), + ), + ), + ), + StacContainer( + color: '#BBDEFB', + child: StacCenter( + child: StacText( + data: 'Show 2', + style: StacTextStyle( + color: '#FFFFFF', + fontSize: 20, + fontWeight: StacFontWeight.w400, + ), + ), + ), + ), + ], + ), + ), + ], + ), + ); +} diff --git a/stac_playground/lib/dsl/center.dart b/stac_playground/lib/dsl/center.dart new file mode 100644 index 00000000..ca8834bb --- /dev/null +++ b/stac_playground/lib/dsl/center.dart @@ -0,0 +1,15 @@ +import 'package:stac_core/stac_core.dart'; + +@StacScreen(screenName: 'center') +StacWidget centerExample() { + return StacScaffold( + appBar: StacAppBar(title: StacText(data: 'Center')), + body: StacCenter( + child: StacContainer( + alignment: StacAlignment.center, + height: 200, + width: 150, + color: '#FC5632', + child: StacText( + data: 'Flutter', style: StacTextStyle(fontSize: 23))))); +} diff --git a/stac_playground/lib/dsl/chip.dart b/stac_playground/lib/dsl/chip.dart new file mode 100644 index 00000000..8829a8c8 --- /dev/null +++ b/stac_playground/lib/dsl/chip.dart @@ -0,0 +1,125 @@ +import 'package:stac_core/stac_core.dart'; + +@StacScreen(screenName: 'chip') +StacWidget chipExample() { + return StacScaffold( + appBar: StacAppBar(title: StacText(data: 'Chip')), + body: StacColumn( + mainAxisAlignment: StacMainAxisAlignment.center, + crossAxisAlignment: StacCrossAxisAlignment.center, + children: [ + StacRow( + mainAxisAlignment: StacMainAxisAlignment.center, + crossAxisAlignment: StacCrossAxisAlignment.center, + children: [ + StacChip( + avatar: StacIcon(iconType: StacIconType.material, icon: 'tune'), + label: StacText(data: 'Chip', style: StacTextStyle(fontSize: 21)), + ), + StacSizedBox(width: 20), + StacChip( + autofocus: true, + deleteIcon: StacIcon( + iconType: StacIconType.material, + icon: 'close', + size: 20, + ), + label: StacText(data: 'Chip', style: StacTextStyle(fontSize: 21)), + ), + ], + ), + StacRow( + mainAxisAlignment: StacMainAxisAlignment.center, + crossAxisAlignment: StacCrossAxisAlignment.center, + children: [ + StacChip( + avatar: StacIcon(iconType: StacIconType.material, icon: 'tune'), + label: StacText( + data: 'Round Chip', + style: StacTextStyle(fontSize: 21), + ), + shape: StacRoundedRectangleBorder( + borderRadius: StacBorderRadius.only( + topLeft: 8, + topRight: 8, + bottomLeft: 8, + bottomRight: 8, + ), + ), + ), + StacSizedBox(width: 20), + StacChip( + autofocus: true, + deleteIcon: StacIcon( + iconType: StacIconType.material, + icon: 'close', + size: 20, + ), + label: StacText( + data: 'Round Chip', + style: StacTextStyle(fontSize: 21), + ), + elevation: 8, + shape: StacRoundedRectangleBorder( + borderRadius: StacBorderRadius.only( + topLeft: 8, + topRight: 8, + bottomLeft: 8, + bottomRight: 8, + ), + ), + ), + ], + ), + StacRow( + mainAxisAlignment: StacMainAxisAlignment.center, + crossAxisAlignment: StacCrossAxisAlignment.center, + children: [ + StacChip( + color: '#6d81b3', + avatar: StacIcon( + color: '#ffffff', + iconType: StacIconType.material, + icon: 'tune', + ), + label: StacText( + data: 'Color Chip', + style: StacTextStyle(fontSize: 21, color: '#ffffff'), + ), + shape: StacRoundedRectangleBorder( + borderRadius: StacBorderRadius.only( + topLeft: 8, + topRight: 8, + bottomLeft: 8, + bottomRight: 8, + ), + ), + ), + StacSizedBox(width: 20), + StacChip( + color: '#6d81b3', + deleteIcon: StacIcon( + color: '#ffffff', + iconType: StacIconType.material, + icon: 'close', + size: 20, + ), + label: StacText( + data: 'Color Chip', + style: StacTextStyle(color: '#ffffff', fontSize: 21), + ), + shape: StacRoundedRectangleBorder( + borderRadius: StacBorderRadius.only( + topLeft: 8, + topRight: 8, + bottomLeft: 8, + bottomRight: 8, + ), + ), + ), + ], + ), + ], + ), + ); +} diff --git a/stac_playground/lib/dsl/circular_progress_indicator.dart b/stac_playground/lib/dsl/circular_progress_indicator.dart new file mode 100644 index 00000000..9feaaa8c --- /dev/null +++ b/stac_playground/lib/dsl/circular_progress_indicator.dart @@ -0,0 +1,29 @@ +import 'package:stac_core/stac_core.dart'; + +@StacScreen(screenName: 'circular_progress_indicator') +StacWidget circularProgressIndicatorExample() { + return StacScaffold( + appBar: StacAppBar(title: StacText(data: 'Circular Progress Indicator')), + body: StacCenter( + child: StacColumn( + crossAxisAlignment: StacCrossAxisAlignment.center, + spacing: 52, + children: [ + StacSizedBox(height: 1), + StacCircularProgressIndicator(color: '#672BFF', strokeWidth: 3), + StacCircularProgressIndicator( + color: '#541204', + strokeWidth: 6, + backgroundColor: '#FFD700', + strokeCap: StacStrokeCap.round, + ), + StacCircularProgressIndicator( + color: '#bd3ed3', + strokeWidth: 3, + value: 0.5, + ), + ], + ), + ), + ); +} diff --git a/stac_playground/lib/dsl/clip_oval.dart b/stac_playground/lib/dsl/clip_oval.dart new file mode 100644 index 00000000..c7897ee7 --- /dev/null +++ b/stac_playground/lib/dsl/clip_oval.dart @@ -0,0 +1,64 @@ +import 'package:stac_core/stac_core.dart'; + +@StacScreen(screenName: 'clip_oval') +StacWidget clipOvalExample() { + return StacScaffold( + appBar: StacAppBar(title: StacText(data: 'ClipOval')), + body: StacPadding( + padding: StacEdgeInsets.all(16.0), + child: StacColumn( + mainAxisAlignment: StacMainAxisAlignment.center, + crossAxisAlignment: StacCrossAxisAlignment.center, + children: [ + StacText( + data: 'Basic ClipOval with Image', + style: + StacTextStyle(fontSize: 18.0, fontWeight: StacFontWeight.w600), + ), + StacSizedBox(height: 16.0), + StacClipOval( + clipBehavior: StacClip.antiAlias, + child: StacImage( + src: 'https://picsum.photos/200', + width: 200, + height: 200, + fit: StacBoxFit.cover, + ), + ), + StacSizedBox(height: 32.0), + StacText( + data: 'ClipOval with Container', + style: + StacTextStyle(fontSize: 18.0, fontWeight: StacFontWeight.w600), + ), + StacSizedBox(height: 16.0), + StacClipOval( + clipBehavior: StacClip.antiAlias, + child: StacContainer(color: '#2196F3', height: 100, width: 200), + ), + StacSizedBox(height: 32.0), + StacText( + data: 'ClipOval with Text', + style: + StacTextStyle(fontSize: 18.0, fontWeight: StacFontWeight.w600), + ), + StacSizedBox(height: 16.0), + StacClipOval( + clipBehavior: StacClip.antiAlias, + child: StacContainer( + color: '#FF5722', + height: 100, + width: 100, + child: StacCenter( + child: StacText( + data: 'Hello', + style: StacTextStyle(color: '#FFFFFF', fontSize: 18.0), + ), + ), + ), + ), + ], + ), + ), + ); +} diff --git a/stac_playground/lib/dsl/column.dart b/stac_playground/lib/dsl/column.dart new file mode 100644 index 00000000..39acb6c7 --- /dev/null +++ b/stac_playground/lib/dsl/column.dart @@ -0,0 +1,38 @@ +import 'package:stac_core/stac_core.dart'; + +@StacScreen(screenName: 'column') +StacWidget columnExample() { + return StacScaffold( + appBar: StacAppBar(title: StacText(data: 'Column')), + body: StacSingleChildScrollView( + child: StacColumn( + mainAxisAlignment: StacMainAxisAlignment.center, + crossAxisAlignment: StacCrossAxisAlignment.center, + spacing: 12, + children: [ + StacContainer( + width: 2000, + height: 200, + color: '#FFCDD2', + child: StacCenter( + child: StacText( + data: 'Red Container', + style: StacTextStyle(color: '#B71C1C')))), + StacContainer( + width: 2000, + height: 200, + color: '#C8E6C9', + child: StacCenter( + child: StacText( + data: 'Green Container', + style: StacTextStyle(color: '#1B5E20')))), + StacContainer( + width: 2000, + height: 200, + color: '#BBDEFB', + child: StacCenter( + child: StacText( + data: 'Blue Container', + style: StacTextStyle(color: '#0D47A1')))) + ]))); +} diff --git a/stac_playground/lib/dsl/conditional.dart b/stac_playground/lib/dsl/conditional.dart new file mode 100644 index 00000000..be81873b --- /dev/null +++ b/stac_playground/lib/dsl/conditional.dart @@ -0,0 +1,290 @@ +import 'package:stac_core/stac_core.dart'; + +@StacScreen(screenName: 'conditional') +StacWidget conditionalExample() { + return StacScaffold( + appBar: StacAppBar(title: StacText(data: 'Conditional Widget Example')), + body: StacSingleChildScrollView( + child: StacColumn( + mainAxisAlignment: StacMainAxisAlignment.center, + crossAxisAlignment: StacCrossAxisAlignment.center, + children: [ + StacPadding( + padding: StacEdgeInsets.all(16), + child: StacText( + data: + 'Conditional Widgets allow you to render different UI based on conditions', + textAlign: StacTextAlign.center, + style: StacTextStyle( + fontSize: 16, + fontWeight: StacFontWeight.w700, + ), + ), + ), + StacDivider(height: 20), + StacPadding( + padding: StacEdgeInsets.all(16), + child: StacText( + data: 'Example 1: Simple Boolean Comparison', + style: StacTextStyle( + fontSize: 18, + fontWeight: StacFontWeight.w700, + ), + ), + ), + StacConditional( + condition: '5 > 3', + ifTrue: StacContainer( + padding: StacEdgeInsets.all(16), + decoration: StacBoxDecoration( + color: '#E1F5FE', + borderRadius: StacBorderRadius.all(8), + ), + child: StacRow( + mainAxisAlignment: StacMainAxisAlignment.center, + children: [ + StacIcon(icon: 'check_circle', color: '#01579B'), + StacSizedBox(width: 8), + StacText( + data: 'Condition is TRUE: 5 is greater than 3', + style: StacTextStyle(color: '#01579B'), + ), + ], + ), + ), + ifFalse: StacContainer( + padding: StacEdgeInsets.all(16), + decoration: StacBoxDecoration( + color: '#FFEBEE', + borderRadius: StacBorderRadius.all(8), + ), + child: StacRow( + mainAxisAlignment: StacMainAxisAlignment.center, + children: [ + StacIcon(icon: 'cancel', color: '#B71C1C'), + StacSizedBox(width: 8), + StacText( + data: 'Condition is FALSE: 5 is not greater than 3', + style: StacTextStyle(color: '#B71C1C'), + ), + ], + ), + ), + ), + StacDivider(height: 20), + StacPadding( + padding: StacEdgeInsets.all(16), + child: StacText( + data: 'Example 2: String Comparison', + style: StacTextStyle( + fontSize: 18, + fontWeight: StacFontWeight.w700, + ), + ), + ), + StacConditional( + condition: 'Flutter == Flutter', + ifTrue: StacContainer( + padding: StacEdgeInsets.all(16), + decoration: StacBoxDecoration( + color: '#E8F5E9', + borderRadius: StacBorderRadius.all(8), + ), + child: StacRow( + mainAxisAlignment: StacMainAxisAlignment.center, + children: [ + StacIcon(icon: 'check_circle', color: '#1B5E20'), + StacSizedBox(width: 8), + StacText( + data: 'Strings are equal', + style: StacTextStyle(color: '#1B5E20'), + ), + ], + ), + ), + ifFalse: StacContainer( + padding: StacEdgeInsets.all(16), + decoration: StacBoxDecoration( + color: '#FFEBEE', + borderRadius: StacBorderRadius.all(8), + ), + child: StacRow( + mainAxisAlignment: StacMainAxisAlignment.center, + children: [ + StacIcon(icon: 'cancel', color: '#B71C1C'), + StacSizedBox(width: 8), + StacText( + data: 'Strings are not equal', + style: StacTextStyle(color: '#B71C1C'), + ), + ], + ), + ), + ), + StacDivider(height: 20), + StacPadding( + padding: StacEdgeInsets.all(16), + child: StacText( + data: 'Example 3: Mathematical Expression', + style: StacTextStyle( + fontSize: 18, + fontWeight: StacFontWeight.w700, + ), + ), + ), + StacConditional( + condition: '(10 + 5) * 2 == 30', + ifTrue: StacContainer( + padding: StacEdgeInsets.all(16), + decoration: StacBoxDecoration( + color: '#E8F5E9', + borderRadius: StacBorderRadius.all(8), + ), + child: StacRow( + mainAxisAlignment: StacMainAxisAlignment.center, + children: [ + StacIcon(icon: 'check_circle', color: '#1B5E20'), + StacSizedBox(width: 8), + StacText( + data: '(10 + 5) * 2 equals 30', + style: StacTextStyle(color: '#1B5E20'), + ), + ], + ), + ), + ifFalse: StacContainer( + padding: StacEdgeInsets.all(16), + decoration: StacBoxDecoration( + color: '#FFEBEE', + borderRadius: StacBorderRadius.all(8), + ), + child: StacRow( + mainAxisAlignment: StacMainAxisAlignment.center, + children: [ + StacIcon(icon: 'cancel', color: '#B71C1C'), + StacSizedBox(width: 8), + StacText( + data: '(10 + 5) * 2 does not equal 30', + style: StacTextStyle(color: '#B71C1C'), + ), + ], + ), + ), + ), + StacDivider(height: 20), + StacPadding( + padding: StacEdgeInsets.all(16), + child: StacText( + data: 'Example 4: Logical Operators', + style: StacTextStyle( + fontSize: 18, + fontWeight: StacFontWeight.w700, + ), + ), + ), + StacConditional( + condition: 'true && (false || true)', + ifTrue: StacContainer( + padding: StacEdgeInsets.all(16), + decoration: StacBoxDecoration( + color: '#E8F5E9', + borderRadius: StacBorderRadius.all(8), + ), + child: StacRow( + mainAxisAlignment: StacMainAxisAlignment.center, + children: [ + StacIcon(icon: 'check_circle', color: '#1B5E20'), + StacSizedBox(width: 8), + StacText( + data: 'Logical expression is TRUE', + style: StacTextStyle(color: '#1B5E20'), + ), + ], + ), + ), + ifFalse: StacContainer( + padding: StacEdgeInsets.all(16), + decoration: StacBoxDecoration( + color: '#FFEBEE', + borderRadius: StacBorderRadius.all(8), + ), + child: StacRow( + mainAxisAlignment: StacMainAxisAlignment.center, + children: [ + StacIcon(icon: 'cancel', color: '#B71C1C'), + StacSizedBox(width: 8), + StacText( + data: 'Logical expression is FALSE', + style: StacTextStyle(color: '#B71C1C'), + ), + ], + ), + ), + ), + StacDivider(height: 20), + StacPadding( + padding: StacEdgeInsets.all(16), + child: StacText( + data: 'Example 5: Nested Conditionals', + style: StacTextStyle( + fontSize: 18, + fontWeight: StacFontWeight.w700, + ), + ), + ), + StacConditional( + condition: '3 < 5', + ifTrue: StacConditional( + condition: '10 > 8', + ifTrue: StacContainer( + padding: StacEdgeInsets.all(16), + decoration: StacBoxDecoration( + color: '#E1F5FE', + borderRadius: StacBorderRadius.all(8), + ), + child: StacText( + data: 'Both conditions are TRUE: 3 < 5 AND 10 > 8', + textAlign: StacTextAlign.center, + style: StacTextStyle( + color: '#01579B', + fontWeight: StacFontWeight.w700, + ), + ), + ), + ifFalse: StacContainer( + padding: StacEdgeInsets.all(16), + decoration: StacBoxDecoration( + color: '#FFF3E0', + borderRadius: StacBorderRadius.all(8), + ), + child: StacText( + data: 'First condition is TRUE, but second is FALSE', + textAlign: StacTextAlign.center, + style: StacTextStyle( + color: '#E65100', + fontWeight: StacFontWeight.w700, + ), + ), + ), + ), + ifFalse: StacContainer( + padding: StacEdgeInsets.all(16), + decoration: StacBoxDecoration( + color: '#FFEBEE', + borderRadius: StacBorderRadius.all(8), + ), + child: StacText( + data: 'First condition is FALSE', + textAlign: StacTextAlign.center, + style: StacTextStyle( + color: '#B71C1C', + fontWeight: StacFontWeight.w700, + ), + ), + ), + ), + ], + ), + ), + ); +} diff --git a/stac_playground/lib/dsl/container.dart b/stac_playground/lib/dsl/container.dart new file mode 100644 index 00000000..f0fa68db --- /dev/null +++ b/stac_playground/lib/dsl/container.dart @@ -0,0 +1,37 @@ +import 'package:stac_core/stac_core.dart'; + +@StacScreen(screenName: 'container') +StacWidget containerExample() { + return StacScaffold( + appBar: StacAppBar(title: StacText(data: 'Container')), + body: StacListView(shrinkWrap: true, children: [ + StacSizedBox(height: 52), + StacCenter( + child: StacContainer( + color: 'primary@50', height: 250, width: double.maxFinite)), + StacSizedBox(height: 52), + StacCenter( + child: StacContainer( + color: '#FC5632', + height: 100, + width: 200, + child: StacAlign( + alignment: StacAlignmentDirectional.bottomCenter, + child: StacText( + data: 'Flutter', + style: StacTextStyle( + fontSize: 23, fontWeight: StacFontWeight.w600))))), + StacSizedBox(height: 52), + StacCenter( + child: StacContainer( + color: '#FFFF00', + height: 250, + width: 250, + child: StacAlign( + alignment: StacAlignmentDirectional.bottomCenter, + child: StacText( + data: 'Flutter', + style: StacTextStyle( + fontSize: 23, fontWeight: StacFontWeight.w600))))) + ])); +} diff --git a/stac_playground/lib/dsl/dialog.dart b/stac_playground/lib/dsl/dialog.dart new file mode 100644 index 00000000..12fb7f09 --- /dev/null +++ b/stac_playground/lib/dsl/dialog.dart @@ -0,0 +1,111 @@ +import 'package:stac_core/stac_core.dart'; + +@StacScreen(screenName: 'dialog') +StacWidget dialogExample() { + return StacScaffold( + appBar: StacAppBar(title: StacText(data: 'Dialogs')), + body: StacRow( + mainAxisAlignment: StacMainAxisAlignment.center, + crossAxisAlignment: StacCrossAxisAlignment.center, + children: [ + StacColumn( + mainAxisAlignment: StacMainAxisAlignment.center, + crossAxisAlignment: StacCrossAxisAlignment.center, + children: [ + StacElevatedButton( + style: StacButtonStyle( + padding: StacEdgeInsets.only( + top: 8, + left: 12, + right: 12, + bottom: 8, + ), + ), + onPressed: StacDialogAction( + widget: StacAlertDialog( + content: StacPadding( + padding: StacEdgeInsets.only( + top: 0, + left: 12, + right: 12, + bottom: 8, + ), + child: StacText( + data: 'Discard draft?', + style: StacTextStyle(fontSize: 14), + ), + ), + actions: [ + StacTextButton( + onPressed: StacNavigateAction( + navigationStyle: NavigationStyle.pop, + ), + child: StacText(data: 'CANCEL'), + ), + StacSizedBox(width: 8), + StacTextButton( + onPressed: StacNavigateAction( + navigationStyle: NavigationStyle.pop, + ), + child: StacText(data: 'DISCARD'), + ), + StacSizedBox(width: 12), + ], + ).toJson(), + ), + child: StacText(data: 'SIMPLE ALERT'), + ), + StacSizedBox(height: 12), + StacElevatedButton( + style: StacButtonStyle( + padding: StacEdgeInsets.only( + top: 8, + left: 12, + right: 12, + bottom: 8, + ), + ), + onPressed: StacDialogAction( + widget: StacAlertDialog( + title: StacText( + data: "Use Google's Location Services?", + style: StacTextStyle(fontSize: 21), + ), + content: StacPadding( + padding: StacEdgeInsets.only( + top: 24, + left: 12, + right: 12, + bottom: 8, + ), + child: StacText( + data: 'Let Google help apps determine location.', + style: StacTextStyle(fontSize: 14), + ), + ), + actions: [ + StacTextButton( + onPressed: StacNavigateAction( + navigationStyle: NavigationStyle.pop, + ), + child: StacText(data: 'DISAGREE'), + ), + StacSizedBox(width: 8), + StacTextButton( + onPressed: StacNavigateAction( + navigationStyle: NavigationStyle.pop, + ), + child: StacText(data: 'AGREE'), + ), + StacSizedBox(width: 12), + ], + ).toJson(), + ), + child: StacText(data: 'ALERT WITH TITLE'), + ), + ], + ), + ], + ), + ); +} diff --git a/stac_playground/lib/dsl/divider.dart b/stac_playground/lib/dsl/divider.dart new file mode 100644 index 00000000..1e95908e --- /dev/null +++ b/stac_playground/lib/dsl/divider.dart @@ -0,0 +1,23 @@ +import 'package:stac_core/stac_core.dart'; + +@StacScreen(screenName: 'divider') +StacWidget dividerExample() { + return StacScaffold( + appBar: StacAppBar(title: StacText(data: 'Divider')), + body: StacListView(children: [ + StacDivider(thickness: 5, height: 5, color: '#672BFF'), + StacSizedBox(height: 20), + StacDivider(thickness: 3, height: 3, color: '#FC5632'), + StacSizedBox(height: 20), + StacDivider(thickness: 2, height: 2, color: '#32FC88'), + StacSizedBox(height: 20), + StacSizedBox( + height: 200, + child: StacVerticalDivider( + width: 20, + thickness: 4, + indent: 10, + endIndent: 10, + color: '#21814C')) + ])); +} diff --git a/stac_playground/lib/dsl/drawer.dart b/stac_playground/lib/dsl/drawer.dart new file mode 100644 index 00000000..85f6940d --- /dev/null +++ b/stac_playground/lib/dsl/drawer.dart @@ -0,0 +1,104 @@ +import 'package:stac_core/stac_core.dart'; + +@StacScreen(screenName: 'drawer') +StacWidget drawerExample() { + return StacScaffold( + appBar: StacAppBar( + title: StacText( + data: 'Drawer Example', + style: StacTextStyle(color: '#ffffff', fontSize: 21), + ), + backgroundColor: '#4D00E9', + ), + drawerEnableOpenDragGesture: true, + drawerEdgeDragWidth: 20.0, + drawer: StacDrawer( + backgroundColor: '#f5f5f5', + elevation: 16.0, + width: 280.0, + child: StacColumn( + children: [ + StacContainer( + height: 120, + color: '#4D00E9', + child: StacCenter( + child: StacText( + data: 'Drawer Header', + style: StacTextStyle( + color: '#ffffff', + fontSize: 20, + fontWeight: StacFontWeight.bold, + ), + ), + ), + ), + StacExpanded( + child: StacListView( + children: [ + StacListTile( + leading: StacIcon( + iconType: StacIconType.material, + icon: 'home', + size: 24, + ), + title: StacText(data: 'Home'), + onTap: StacAction( + jsonData: { + 'actionType': 'snackBar', + 'content': 'Home tapped!' + }, + ), + ), + StacListTile( + leading: StacIcon( + iconType: StacIconType.material, + icon: 'settings', + size: 24, + ), + title: StacText(data: 'Settings'), + onTap: StacAction( + jsonData: { + 'actionType': 'snackBar', + 'content': 'Settings tapped!' + }, + ), + ), + StacListTile( + leading: StacIcon( + iconType: StacIconType.material, + icon: 'info', + size: 24, + ), + title: StacText(data: 'About'), + onTap: StacAction( + jsonData: { + 'actionType': 'snackBar', + 'content': 'About tapped!' + }, + ), + ), + ], + ), + ), + ], + ), + ), + body: StacCenter( + child: StacColumn( + mainAxisAlignment: StacMainAxisAlignment.center, + children: [ + StacText( + data: 'Welcome to Drawer Example', + style: StacTextStyle(fontSize: 24, fontWeight: StacFontWeight.bold), + ), + StacSizedBox(height: 16), + StacText( + data: + 'Swipe from left edge or tap the menu icon to open the drawer', + style: StacTextStyle(fontSize: 16, color: '#666666'), + ), + ], + ), + ), + ); +} diff --git a/stac_playground/lib/dsl/dropdown_menu_view.dart b/stac_playground/lib/dsl/dropdown_menu_view.dart new file mode 100644 index 00000000..fe2b0455 --- /dev/null +++ b/stac_playground/lib/dsl/dropdown_menu_view.dart @@ -0,0 +1,55 @@ +import 'package:stac_core/stac_core.dart'; + +@StacScreen(screenName: 'dropdown_menu_view') +StacWidget dropdownMenuViewExample() { + return StacScaffold( + appBar: StacAppBar(title: StacText(data: 'Stac DropDown')), + body: StacColumn( + children: [ + StacDropdownMenu( + leadingIcon: StacIcon( + iconType: StacIconType.material, + icon: 'arrow_downward', + size: 32, + ), + trailingIcon: StacIcon( + iconType: StacIconType.material, + icon: 'double_arrow', + size: 32, + ), + initialSelection: 'b', + dropdownMenuEntries: [ + StacDropdownMenuEntry( + label: 'A', + value: 'a', + leadingIcon: StacIcon( + iconType: StacIconType.material, + icon: 'arrow_downward_sharp', + size: 32, + ), + trailingIcon: StacIcon( + iconType: StacIconType.material, + icon: 'arrow_forward_ios', + size: 32, + ), + ), + StacDropdownMenuEntry( + label: 'B', + value: 'b', + leadingIcon: StacIcon( + iconType: StacIconType.material, + icon: 'arrow_downward_sharp', + size: 32, + ), + trailingIcon: StacIcon( + iconType: StacIconType.material, + icon: 'arrow_forward_ios', + size: 32, + ), + ), + ], + ), + ], + ), + ); +} diff --git a/stac_playground/lib/dsl/dynamic_list_view.dart b/stac_playground/lib/dsl/dynamic_list_view.dart new file mode 100644 index 00000000..889726ca --- /dev/null +++ b/stac_playground/lib/dsl/dynamic_list_view.dart @@ -0,0 +1,28 @@ +import 'package:stac_core/stac_core.dart'; + +@StacScreen(screenName: 'dynamic_list_view') +StacWidget dynamicListViewExample() { + return StacScaffold( + appBar: StacAppBar(title: StacText(data: 'Users List')), + body: StacDynamicView( + request: StacNetworkRequest( + url: 'https://dummyjson.com/users', + method: Method.get, + ), + targetPath: 'users', + // NOTE: the JSON template is a listView carrying an `itemTemplate` key — + // a runtime-only field the dynamicView parser reads to render each list + // item. StacListView (stac_core) has no `itemTemplate` param, so the + // per-item template is represented as the list's single child here. + template: StacListView( + children: [ + StacListTile( + title: StacText(data: '{{firstName}} {{lastName}}'), + subtitle: StacText(data: '{{email}}'), + leading: StacCircleAvatar(backgroundImage: '{{image}}'), + ), + ], + ), + ), + ); +} diff --git a/stac_playground/lib/dsl/dynamic_view.dart b/stac_playground/lib/dsl/dynamic_view.dart new file mode 100644 index 00000000..d6e06ba5 --- /dev/null +++ b/stac_playground/lib/dsl/dynamic_view.dart @@ -0,0 +1,58 @@ +import 'package:stac_core/stac_core.dart'; + +@StacScreen(screenName: 'dynamic_view') +StacWidget dynamicViewExample() { + return StacScaffold( + appBar: StacAppBar(title: StacText(data: 'User Profile')), + body: StacDynamicView( + request: StacNetworkRequest( + url: 'https://dummyjson.com/users/1', + method: Method.get, + ), + loaderWidget: StacCenter( + child: StacColumn( + children: [ + StacText(data: 'Loading...'), + StacCircularProgressIndicator(), + ], + ), + ), + errorWidget: StacCenter( + child: StacText(data: 'Error fetching user profile'), + ), + template: StacColumn( + children: [ + StacContainer( + padding: StacEdgeInsets.all(16), + child: StacColumn( + crossAxisAlignment: StacCrossAxisAlignment.start, + children: [ + StacImage(src: '{{image}}', width: 100, height: 100), + StacText( + data: '{{firstName}} {{lastName}}', + style: StacTextStyle( + fontSize: 24, + fontWeight: StacFontWeight.w700, + ), + ), + StacSizedBox(height: 8), + StacText( + data: 'Email: {{email}}', + style: StacTextStyle(fontSize: 16, color: '#666666'), + ), + StacText( + data: 'Phone: {{phone}}', + style: StacTextStyle(fontSize: 16, color: '#666666'), + ), + StacText( + data: 'Age: {{age}}', + style: StacTextStyle(fontSize: 16, color: '#666666'), + ), + ], + ), + ), + ], + ), + ), + ); +} diff --git a/stac_playground/lib/dsl/elevated_button.dart b/stac_playground/lib/dsl/elevated_button.dart new file mode 100644 index 00000000..b77a9e49 --- /dev/null +++ b/stac_playground/lib/dsl/elevated_button.dart @@ -0,0 +1,104 @@ +import 'package:stac_core/stac_core.dart'; + +@StacScreen(screenName: 'elevated_button') +StacWidget elevatedButtonExample() { + return StacScaffold( + appBar: StacAppBar(title: StacText(data: 'Elevated Button')), + body: StacRow( + mainAxisAlignment: StacMainAxisAlignment.center, + crossAxisAlignment: StacCrossAxisAlignment.center, + children: [ + StacColumn( + mainAxisAlignment: StacMainAxisAlignment.center, + crossAxisAlignment: StacCrossAxisAlignment.center, + children: [ + StacRow( + mainAxisAlignment: StacMainAxisAlignment.center, + crossAxisAlignment: StacCrossAxisAlignment.center, + children: [ + StacElevatedButton( + child: StacText(data: 'BUTTON'), + style: StacButtonStyle( + padding: StacEdgeInsets.only( + top: 8, left: 12, right: 12, bottom: 8)), + onPressed: StacAction()), + StacSizedBox(width: 20), + StacElevatedButton( + child: StacRow(children: [ + StacIcon( + iconType: StacIconType.material, + icon: 'add', + size: 18), + StacSizedBox(width: 4), + StacText(data: 'BUTTON') + ]), + style: StacButtonStyle( + padding: StacEdgeInsets.only( + top: 8, left: 12, right: 12, bottom: 8)), + onPressed: StacAction()) + ]), + StacSizedBox(height: 12), + StacRow( + mainAxisAlignment: StacMainAxisAlignment.center, + crossAxisAlignment: StacCrossAxisAlignment.center, + children: [ + StacElevatedButton( + child: StacText(data: 'BUTTON'), + style: StacButtonStyle( + padding: StacEdgeInsets.only( + top: 8, left: 12, right: 12, bottom: 8))), + StacSizedBox(width: 20), + StacElevatedButton( + child: StacRow(children: [ + StacIcon( + iconType: StacIconType.material, + icon: 'add', + size: 18), + StacSizedBox(width: 4), + StacText(data: 'BUTTON') + ]), + style: StacButtonStyle( + padding: StacEdgeInsets.only( + top: 8, left: 12, right: 12, bottom: 8))) + ]), + StacSizedBox(height: 12), + StacRow( + mainAxisAlignment: StacMainAxisAlignment.center, + crossAxisAlignment: StacCrossAxisAlignment.center, + children: [ + StacElevatedButton( + child: StacText(data: 'BUTTON'), + style: StacButtonStyle( + padding: StacEdgeInsets.only( + top: 8, left: 12, right: 12, bottom: 8), + shape: StacRoundedRectangleBorder( + borderRadius: StacBorderRadius.only( + topLeft: 8, + topRight: 8, + bottomLeft: 8, + bottomRight: 8))), + onPressed: StacAction()), + StacSizedBox(width: 20), + StacElevatedButton( + child: StacRow(children: [ + StacIcon( + iconType: StacIconType.material, + icon: 'add', + size: 18), + StacSizedBox(width: 4), + StacText(data: 'BUTTON') + ]), + style: StacButtonStyle( + padding: StacEdgeInsets.only( + top: 8, left: 12, right: 12, bottom: 8), + shape: StacRoundedRectangleBorder( + borderRadius: StacBorderRadius.only( + topLeft: 8, + topRight: 8, + bottomLeft: 8, + bottomRight: 8))), + onPressed: StacAction()) + ]) + ]) + ])); +} diff --git a/stac_playground/lib/dsl/filled_button.dart b/stac_playground/lib/dsl/filled_button.dart new file mode 100644 index 00000000..08e60869 --- /dev/null +++ b/stac_playground/lib/dsl/filled_button.dart @@ -0,0 +1,142 @@ +import 'package:stac_core/stac_core.dart'; + +@StacScreen(screenName: 'filled_button') +StacWidget filledButtonExample() { + return StacScaffold( + appBar: StacAppBar(title: StacText(data: 'Filled Button')), + body: StacRow( + mainAxisAlignment: StacMainAxisAlignment.center, + crossAxisAlignment: StacCrossAxisAlignment.center, + children: [ + StacColumn( + mainAxisAlignment: StacMainAxisAlignment.center, + crossAxisAlignment: StacCrossAxisAlignment.center, + children: [ + StacRow( + mainAxisAlignment: StacMainAxisAlignment.center, + crossAxisAlignment: StacCrossAxisAlignment.center, + children: [ + StacFilledButton( + child: StacText(data: 'BUTTON'), + style: StacButtonStyle( + padding: StacEdgeInsets.only( + top: 8, left: 12, right: 12, bottom: 8)), + onPressed: StacAction()), + StacSizedBox(width: 20), + StacFilledButton( + child: StacRow(children: [ + StacIcon( + iconType: StacIconType.material, + icon: 'add', + size: 18), + StacSizedBox(width: 4), + StacText(data: 'BUTTON') + ]), + style: StacButtonStyle( + padding: StacEdgeInsets.only( + top: 8, left: 12, right: 12, bottom: 8)), + onPressed: StacAction()) + ]), + StacSizedBox(height: 12), + StacRow( + mainAxisAlignment: StacMainAxisAlignment.center, + crossAxisAlignment: StacCrossAxisAlignment.center, + children: [ + StacFilledButton( + child: StacText(data: 'BUTTON'), + style: StacButtonStyle( + padding: StacEdgeInsets.only( + top: 8, left: 12, right: 12, bottom: 8), + shape: StacRoundedRectangleBorder( + borderRadius: StacBorderRadius.only( + topLeft: 8, + topRight: 8, + bottomLeft: 8, + bottomRight: 8))), + onPressed: StacAction()), + StacSizedBox(width: 20), + StacFilledButton( + child: StacRow(children: [ + StacIcon( + iconType: StacIconType.material, + icon: 'add', + size: 18), + StacSizedBox(width: 4), + StacText(data: 'BUTTON') + ]), + style: StacButtonStyle( + padding: StacEdgeInsets.only( + top: 8, left: 12, right: 12, bottom: 8), + shape: StacRoundedRectangleBorder( + borderRadius: StacBorderRadius.only( + topLeft: 8, + topRight: 8, + bottomLeft: 8, + bottomRight: 8))), + onPressed: StacAction()) + ]), + StacSizedBox(height: 12), + StacRow( + mainAxisAlignment: StacMainAxisAlignment.center, + crossAxisAlignment: StacCrossAxisAlignment.center, + children: [ + StacFilledButton( + child: StacText(data: 'BUTTON'), + style: StacButtonStyle( + padding: StacEdgeInsets.only( + top: 8, left: 12, right: 12, bottom: 8))), + StacSizedBox(width: 20), + StacFilledButton( + child: StacRow(children: [ + StacIcon( + iconType: StacIconType.material, + icon: 'add', + size: 18), + StacSizedBox(width: 4), + StacText(data: 'BUTTON') + ]), + style: StacButtonStyle( + padding: StacEdgeInsets.only( + top: 8, left: 12, right: 12, bottom: 8))) + ]), + StacSizedBox(height: 12), + StacRow( + mainAxisAlignment: StacMainAxisAlignment.center, + crossAxisAlignment: StacCrossAxisAlignment.center, + children: [ + StacFilledButton( + child: StacText(data: 'BUTTON'), + style: StacButtonStyle( + padding: StacEdgeInsets.only( + top: 8, left: 12, right: 12, bottom: 8), + shape: StacRoundedRectangleBorder( + borderRadius: StacBorderRadius.only( + topLeft: 8, + topRight: 8, + bottomLeft: 8, + bottomRight: 8))), + onPressed: StacAction()), + StacSizedBox(width: 20), + StacFilledButton( + child: StacRow(children: [ + StacIcon( + iconType: StacIconType.material, + icon: 'add', + size: 18), + StacSizedBox(width: 4), + StacText(data: 'BUTTON') + ]), + style: StacButtonStyle( + padding: StacEdgeInsets.only( + top: 8, left: 12, right: 12, bottom: 8), + shape: StacRoundedRectangleBorder( + borderRadius: StacBorderRadius.only( + topLeft: 8, + topRight: 8, + bottomLeft: 8, + bottomRight: 8))), + onPressed: StacAction()) + ]) + ]) + ])); +} diff --git a/stac_playground/lib/dsl/fitted_box.dart b/stac_playground/lib/dsl/fitted_box.dart new file mode 100644 index 00000000..d891639d --- /dev/null +++ b/stac_playground/lib/dsl/fitted_box.dart @@ -0,0 +1,26 @@ +import 'package:stac_core/stac_core.dart'; + +@StacScreen(screenName: 'fitted_box') +StacWidget fittedBoxExample() { + return StacScaffold( + appBar: StacAppBar(title: StacText(data: 'FittedBox')), + body: StacPadding( + padding: StacEdgeInsets.only(top: 12, left: 12, right: 12), + child: StacColumn( + mainAxisAlignment: StacMainAxisAlignment.start, + crossAxisAlignment: StacCrossAxisAlignment.start, + children: [ + StacSizedBox(height: 12), + StacFittedBox( + fit: StacBoxFit.contain, + alignment: StacAlignment.center, + child: StacText( + data: 'Hello, World!', + style: StacTextStyle(fontSize: 20, color: '#000000'), + ), + ), + ], + ), + ), + ); +} diff --git a/stac_playground/lib/dsl/floating_action_button.dart b/stac_playground/lib/dsl/floating_action_button.dart new file mode 100644 index 00000000..04766105 --- /dev/null +++ b/stac_playground/lib/dsl/floating_action_button.dart @@ -0,0 +1,57 @@ +import 'package:stac_core/stac_core.dart'; + +@StacScreen(screenName: 'floating_action_button') +StacWidget floatingActionButtonExample() { + return StacScaffold( + appBar: StacAppBar(title: StacText(data: 'Floating Action Button')), + body: StacRow( + mainAxisAlignment: StacMainAxisAlignment.center, + crossAxisAlignment: StacCrossAxisAlignment.center, + children: [ + StacColumn( + mainAxisAlignment: StacMainAxisAlignment.center, + crossAxisAlignment: StacCrossAxisAlignment.center, + children: [ + StacRow( + mainAxisAlignment: StacMainAxisAlignment.center, + crossAxisAlignment: StacCrossAxisAlignment.center, + children: [ + StacFloatingActionButton( + buttonType: StacFloatingActionButtonType.extended, + icon: StacIcon(iconType: StacIconType.material, icon: 'add'), + child: StacText(data: 'Create'), + onPressed: StacAction(), + ), + StacSizedBox(width: 20), + StacFloatingActionButton( + buttonType: StacFloatingActionButtonType.large, + child: StacIcon(iconType: StacIconType.material, icon: 'add'), + onPressed: StacAction(), + ), + ], + ), + StacSizedBox(height: 52), + StacRow( + mainAxisAlignment: StacMainAxisAlignment.center, + crossAxisAlignment: StacCrossAxisAlignment.center, + children: [ + StacFloatingActionButton( + buttonType: StacFloatingActionButtonType.extended, + disabledElevation: 0, + icon: StacIcon(iconType: StacIconType.material, icon: 'add'), + child: StacText(data: 'Create'), + ), + StacSizedBox(width: 20), + StacFloatingActionButton( + buttonType: StacFloatingActionButtonType.large, + disabledElevation: 0, + child: StacIcon(iconType: StacIconType.material, icon: 'add'), + ), + ], + ), + ], + ), + ], + ), + ); +} diff --git a/stac_playground/lib/dsl/form.dart b/stac_playground/lib/dsl/form.dart new file mode 100644 index 00000000..7012a7a5 --- /dev/null +++ b/stac_playground/lib/dsl/form.dart @@ -0,0 +1,101 @@ +import 'package:stac_core/stac_core.dart'; + +@StacScreen(screenName: 'form') +StacWidget formExample() { + return StacScaffold( + appBar: StacAppBar(title: StacText(data: 'Sign in Form')), + body: StacPadding( + padding: StacEdgeInsets.all(12), + child: StacSingleChildScrollView( + child: StacForm( + child: StacColumn( + children: [ + StacTextFormField( + id: 'username', + initialValue: 'kminchelle', + keyboardType: StacTextInputType.text, + textInputAction: StacTextInputAction.next, + maxLines: 1, + decoration: StacInputDecoration(hintText: 'Username'), + validatorRules: [ + StacFormFieldValidator( + rule: 'isAlphanumeric', + message: 'Letters and numbers only', + ), + StacFormFieldValidator( + rule: 'isLength', + options: {'min': 8, 'max': 20}, + message: 'Username must be 8-20 characters', + ), + ], + ), + StacTextFormField( + id: 'password', + keyboardType: StacTextInputType.visiblePassword, + initialValue: '0lelplR', + textInputAction: StacTextInputAction.done, + maxLines: 1, + decoration: StacInputDecoration(hintText: 'Password'), + autovalidateMode: StacAutovalidateMode.onUserInteraction, + validatorRules: [ + StacFormFieldValidator( + rule: 'isLength', + options: {'min': 1}, + message: 'Password is required', + ), + ], + ), + StacSizedBox(height: 24), + StacElevatedButton( + child: StacText(data: 'Sign in'), + style: StacButtonStyle( + backgroundColor: 'primary', + foregroundColor: '#ffffff', + ), + onPressed: StacFormValidate( + isValid: StacNetworkRequest( + url: 'https://dummyjson.com/auth/login', + method: Method.post, + contentType: 'application/json', + body: { + 'username': { + 'actionType': 'getFormValue', + 'id': 'username', + }, + 'password': { + 'actionType': 'getFormValue', + 'id': 'password', + }, + }, + results: [ + StacNetworkResult( + statusCode: 200, + action: { + 'actionType': 'showDialog', + 'widget': { + 'type': 'alertDialog', + 'title': {'type': 'text', 'data': 'Successful'}, + }, + }, + ), + StacNetworkResult( + statusCode: 400, + action: { + 'actionType': 'showDialog', + 'widget': { + 'type': 'alertDialog', + 'title': {'type': 'text', 'data': 'Error'}, + }, + }, + ), + ], + ), + ), + ), + ], + ), + ), + ), + ), + ); +} diff --git a/stac_playground/lib/dsl/fractionally_sized_box.dart b/stac_playground/lib/dsl/fractionally_sized_box.dart new file mode 100644 index 00000000..c5871f04 --- /dev/null +++ b/stac_playground/lib/dsl/fractionally_sized_box.dart @@ -0,0 +1,22 @@ +import 'package:stac_core/stac_core.dart'; + +@StacScreen(screenName: 'fractionally_sized_box') +StacWidget fractionallySizedBoxExample() { + return StacScaffold( + appBar: StacAppBar(title: StacText(data: 'Fractionally SizedBox')), + body: StacContainer( + height: 400, + width: 350, + color: '#A9A9D9', + child: StacFractionallySizedBox( + heightFactor: 0.3, + widthFactor: 0.8, + alignment: StacAlignment.bottomRight, + child: StacElevatedButton( + child: StacText(data: 'FLUTTER'), + onPressed: StacAction(), + ), + ), + ), + ); +} diff --git a/stac_playground/lib/dsl/grid_view.dart b/stac_playground/lib/dsl/grid_view.dart new file mode 100644 index 00000000..80da8ffa --- /dev/null +++ b/stac_playground/lib/dsl/grid_view.dart @@ -0,0 +1,96 @@ +import 'package:stac_core/stac_core.dart'; + +@StacScreen(screenName: 'grid_view') +StacWidget gridViewExample() { + return StacScaffold( + appBar: StacAppBar(title: StacText(data: 'Grid View Example')), + body: StacPadding( + padding: + StacEdgeInsets.only(left: 10, top: 10, right: 10, bottom: 10), + child: StacGridView( + crossAxisCount: 2, + crossAxisSpacing: 10, + mainAxisSpacing: 10, + children: [ + StacContainer( + decoration: StacBoxDecoration( + color: '#FFCDD2', + borderRadius: StacBorderRadius.all(8)), + child: StacCenter( + child: StacText( + data: 'Item 1', + style: StacTextStyle(color: '#B71C1C')))), + StacContainer( + decoration: StacBoxDecoration( + color: '#C8E6C9', + borderRadius: StacBorderRadius.all(8)), + child: StacCenter( + child: StacText( + data: 'Item 2', + style: StacTextStyle(color: '#1B5E20')))), + StacContainer( + decoration: StacBoxDecoration( + color: '#BBDEFB', + borderRadius: StacBorderRadius.all(8)), + child: StacCenter( + child: StacText( + data: 'Item 3', + style: StacTextStyle(color: '#0D47A1')))), + StacContainer( + decoration: StacBoxDecoration( + color: '#FFF9C4', + borderRadius: StacBorderRadius.all(8)), + child: StacCenter( + child: StacText( + data: 'Item 4', + style: StacTextStyle(color: '#F57F17')))), + StacContainer( + decoration: StacBoxDecoration( + color: '#FFCCBC', + borderRadius: StacBorderRadius.all(8)), + child: StacCenter( + child: StacText( + data: 'Item 5', + style: StacTextStyle(color: '#BF360C')))), + StacContainer( + decoration: StacBoxDecoration( + color: '#B2EBF2', + borderRadius: StacBorderRadius.all(8)), + child: StacCenter( + child: StacText( + data: 'Item 6', + style: StacTextStyle(color: '#006064')))), + StacContainer( + decoration: StacBoxDecoration( + color: '#F8BBD0', + borderRadius: StacBorderRadius.all(8)), + child: StacCenter( + child: StacText( + data: 'Item 7', + style: StacTextStyle(color: '#880E4F')))), + StacContainer( + decoration: StacBoxDecoration( + color: '#D1C4E9', + borderRadius: StacBorderRadius.all(8)), + child: StacCenter( + child: StacText( + data: 'Item 8', + style: StacTextStyle(color: '#311B92')))), + StacContainer( + decoration: StacBoxDecoration( + color: '#C5CAE9', + borderRadius: StacBorderRadius.all(8)), + child: StacCenter( + child: StacText( + data: 'Item 9', + style: StacTextStyle(color: '#1A237E')))), + StacContainer( + decoration: StacBoxDecoration( + color: '#FFE0B2', + borderRadius: StacBorderRadius.all(8)), + child: StacCenter( + child: StacText( + data: 'Item 10', + style: StacTextStyle(color: '#E65100')))) + ]))); +} diff --git a/stac_playground/lib/dsl/hero.dart b/stac_playground/lib/dsl/hero.dart new file mode 100644 index 00000000..2822352c --- /dev/null +++ b/stac_playground/lib/dsl/hero.dart @@ -0,0 +1,92 @@ +import 'package:stac_core/stac_core.dart'; + +@StacScreen(screenName: 'hero') +StacWidget heroExample() { + return StacScaffold( + appBar: StacAppBar(title: StacText(data: 'Hero Example')), + body: StacCenter( + child: StacColumn( + mainAxisAlignment: StacMainAxisAlignment.center, + children: [ + StacHero( + tag: 'hero-icon', + createRectTween: StacRectTween( + type: 'materialRectArcTween', + begin: StacRect( + rectType: StacRectType.fromCenter, + center: StacOffset(dx: 120.0, dy: 140.0), + width: 100.0, + height: 100.0, + ), + end: StacRect( + rectType: StacRectType.fromCenter, + center: StacOffset(dx: 200.0, dy: 200.0), + width: 50.0, + height: 50.0, + ), + ), + child: StacIcon( + iconType: StacIconType.material, + icon: 'flight_takeoff', + size: 150.0, + ), + ), + StacSizedBox(height: 24.0), + StacTextButton( + child: StacText(data: 'Tap to see Hero Animation'), + onPressed: StacNavigateAction( + navigationStyle: NavigationStyle.push, + widgetJson: { + 'type': 'scaffold', + 'appBar': { + 'type': 'appBar', + 'title': {'type': 'text', 'data': 'Flight Details'}, + }, + 'body': { + 'type': 'center', + 'child': { + 'type': 'column', + 'mainAxisAlignment': 'center', + 'children': [ + { + 'type': 'hero', + 'tag': 'hero-icon', + 'createRectTween': { + 'type': 'materialRectArcTween', + 'begin': { + 'rectType': 'fromCenter', + 'center': {'dx': 120.0, 'dy': 140.0}, + 'width': 100.0, + 'height': 100.0, + }, + 'end': { + 'rectType': 'fromCenter', + 'center': {'dx': 200.0, 'dy': 200.0}, + 'width': 50.0, + 'height': 50.0, + }, + }, + 'child': { + 'type': 'icon', + 'iconType': 'material', + 'icon': 'flight_takeoff', + 'size': 50.0, + }, + }, + {'type': 'sizedBox', 'height': 16.0}, + { + 'type': 'text', + 'data': 'Flight AB123', + 'style': {'fontSize': 24.0, 'fontWeight': 'w500'}, + }, + ], + }, + }, + }, + ), + ), + ], + ), + ), + ); +} diff --git a/stac_playground/lib/dsl/icon.dart b/stac_playground/lib/dsl/icon.dart new file mode 100644 index 00000000..e766f7e2 --- /dev/null +++ b/stac_playground/lib/dsl/icon.dart @@ -0,0 +1,48 @@ +import 'package:stac_core/stac_core.dart'; + +@StacScreen(screenName: 'icon') +StacWidget iconExample() { + return StacScaffold( + appBar: StacAppBar(title: StacText(data: 'Icon')), + body: StacRow( + mainAxisAlignment: StacMainAxisAlignment.center, + crossAxisAlignment: StacCrossAxisAlignment.center, + children: [ + StacColumn( + mainAxisAlignment: StacMainAxisAlignment.center, + crossAxisAlignment: StacCrossAxisAlignment.center, + children: [ + StacRow( + mainAxisAlignment: StacMainAxisAlignment.center, + crossAxisAlignment: StacCrossAxisAlignment.center, + children: [ + StacIcon( + iconType: StacIconType.material, + icon: 'add', + size: 32), + StacSizedBox(width: 20), + StacIcon( + iconType: StacIconType.material, + icon: 'remove', + size: 32) + ]), + StacSizedBox(height: 24), + StacRow( + mainAxisAlignment: StacMainAxisAlignment.center, + crossAxisAlignment: StacCrossAxisAlignment.center, + children: [ + StacContainer( + child: StacIcon( + iconType: StacIconType.cupertino, + icon: 'add', + size: 32)), + StacSizedBox(width: 20), + StacContainer( + child: StacIcon( + iconType: StacIconType.cupertino, + icon: 'minus', + size: 32)) + ]) + ]) + ])); +} diff --git a/stac_playground/lib/dsl/icon_button.dart b/stac_playground/lib/dsl/icon_button.dart new file mode 100644 index 00000000..c1e2905f --- /dev/null +++ b/stac_playground/lib/dsl/icon_button.dart @@ -0,0 +1,46 @@ +import 'package:stac_core/stac_core.dart'; + +@StacScreen(screenName: 'icon_button') +StacWidget iconButtonExample() { + return StacScaffold( + appBar: StacAppBar(title: StacText(data: 'Icon Button')), + body: StacRow( + mainAxisAlignment: StacMainAxisAlignment.center, + crossAxisAlignment: StacCrossAxisAlignment.center, + children: [ + StacColumn( + mainAxisAlignment: StacMainAxisAlignment.center, + crossAxisAlignment: StacCrossAxisAlignment.center, + children: [ + StacRow( + mainAxisAlignment: StacMainAxisAlignment.center, + crossAxisAlignment: StacCrossAxisAlignment.center, + children: [ + StacIconButton( + icon: StacIcon( + iconType: StacIconType.material, icon: 'add'), + onPressed: StacAction()), + StacSizedBox(width: 20), + StacIconButton( + icon: StacIcon( + iconType: StacIconType.material, + icon: 'remove'), + onPressed: StacAction()) + ]), + StacSizedBox(height: 52), + StacRow( + mainAxisAlignment: StacMainAxisAlignment.center, + crossAxisAlignment: StacCrossAxisAlignment.center, + children: [ + StacIconButton( + icon: StacIcon( + iconType: StacIconType.material, icon: 'add')), + StacSizedBox(width: 20), + StacIconButton( + icon: StacIcon( + iconType: StacIconType.material, + icon: 'remove')) + ]) + ]) + ])); +} diff --git a/stac_playground/lib/dsl/image.dart b/stac_playground/lib/dsl/image.dart new file mode 100644 index 00000000..b02a5e07 --- /dev/null +++ b/stac_playground/lib/dsl/image.dart @@ -0,0 +1,87 @@ +import 'package:stac_core/stac_core.dart'; + +@StacScreen(screenName: 'image') +StacWidget imageExample() { + return StacScaffold( + appBar: StacAppBar(title: StacText(data: 'Image')), + body: StacSingleChildScrollView( + child: StacRow( + mainAxisAlignment: StacMainAxisAlignment.center, + crossAxisAlignment: StacCrossAxisAlignment.center, + children: [ + StacColumn( + mainAxisAlignment: StacMainAxisAlignment.center, + crossAxisAlignment: StacCrossAxisAlignment.center, + children: [ + StacSizedBox(height: 24), + StacRow( + mainAxisAlignment: StacMainAxisAlignment.center, + crossAxisAlignment: StacCrossAxisAlignment.center, + children: [ + StacGestureDetector( + child: StacImage( + src: + 'https://images.pexels.com/photos/15113967/pexels-photo-15113967.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=2', + height: 150), + onTap: StacNavigateAction( + assetPath: 'assets/json/form_example.json')), + StacSizedBox(width: 20), + StacImage( + src: 'assets/images/logo_console.png', + imageType: StacImageType.asset, + height: 150) + ]), + StacSizedBox(height: 24), + StacRow( + mainAxisAlignment: StacMainAxisAlignment.center, + crossAxisAlignment: StacCrossAxisAlignment.center, + children: [ + StacImage( + src: + 'https://images.pexels.com/photos/15352100/pexels-photo-15352100.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=2', + height: 150), + StacSizedBox(width: 20), + StacImage( + src: + 'https://images.pexels.com/photos/15373031/pexels-photo-15373031.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=2', + height: 150) + ]), + StacSizedBox(height: 24), + StacRow( + mainAxisAlignment: StacMainAxisAlignment.center, + crossAxisAlignment: StacCrossAxisAlignment.center, + children: [ + StacImage( + src: + 'https://images.pexels.com/photos/10041677/pexels-photo-10041677.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=2', + height: 150), + StacSizedBox(width: 20), + StacImage( + src: 'assets/images/dart_logo.png', + imageType: StacImageType.asset, + height: 150, + width: 100, + fit: StacBoxFit.fill) + ]), + StacSizedBox(height: 24), + StacRow( + mainAxisAlignment: StacMainAxisAlignment.center, + crossAxisAlignment: StacCrossAxisAlignment.center, + children: [ + StacImage( + src: + 'http://upload.wikimedia.org/wikipedia/commons/0/02/SVG_logo.svg', + height: 100, + width: 100), + StacSizedBox(width: 20), + StacImage( + imageType: StacImageType.asset, + src: 'assets/images/logo.svg', + color: 'primary', + height: 100, + width: 100) + ]), + StacSizedBox(height: 24) + ]) + ]))); +} diff --git a/stac_playground/lib/dsl/inkwell.dart b/stac_playground/lib/dsl/inkwell.dart new file mode 100644 index 00000000..0240d0bf --- /dev/null +++ b/stac_playground/lib/dsl/inkwell.dart @@ -0,0 +1,40 @@ +import 'package:stac_core/stac_core.dart'; + +@StacScreen(screenName: 'inkwell') +StacWidget inkwellExample() { + return StacScaffold( + appBar: StacAppBar(title: StacText(data: 'InkWell')), + body: StacListView( + shrinkWrap: true, + children: [ + StacCenter( + child: StacInkWell( + child: StacPadding( + padding: + StacEdgeInsets.only(top: 20, bottom: 20, right: 20, left: 20), + child: StacText( + data: 'Hello, World! from Inkwell', + textAlign: StacTextAlign.center, + ), + ), + splashColor: '#E1BEE7', + borderRadius: StacBorderRadius.only( + topLeft: 20, + topRight: 20, + bottomLeft: 20, + bottomRight: 20, + ), + radius: 20, + hoverDuration: StacDuration(seconds: 10), + onTap: StacDialogAction( + widget: { + 'type': 'alertDialog', + 'title': {'type': 'text', 'data': 'On Tap Successful'}, + }, + ), + ), + ), + ], + ), + ); +} diff --git a/stac_playground/lib/dsl/limited_box.dart b/stac_playground/lib/dsl/limited_box.dart new file mode 100644 index 00000000..a7788c04 --- /dev/null +++ b/stac_playground/lib/dsl/limited_box.dart @@ -0,0 +1,17 @@ +import 'package:stac_core/stac_core.dart'; + +@StacScreen(screenName: 'limited_box') +StacWidget limitedBoxExample() { + return StacScaffold( + appBar: StacAppBar(title: StacText(data: 'LimitedBox')), + body: StacListView(shrinkWrap: true, children: [ + StacSizedBox(height: 25), + StacLimitedBox( + child: StacContainer( + height: 100, + color: '#FF0000', + child: StacText( + data: 'Hello, World! from Limited Box', + style: StacTextStyle(fontSize: 16, color: '#000000')))) + ])); +} diff --git a/stac_playground/lib/dsl/linear_progress_indicator.dart b/stac_playground/lib/dsl/linear_progress_indicator.dart new file mode 100644 index 00000000..4f1b8730 --- /dev/null +++ b/stac_playground/lib/dsl/linear_progress_indicator.dart @@ -0,0 +1,27 @@ +import 'package:stac_core/stac_core.dart'; + +@StacScreen(screenName: 'linear_progress_indicator') +StacWidget linearProgressIndicatorExample() { + return StacScaffold( + appBar: StacAppBar(title: StacText(data: 'Linear Progress Indicator')), + body: StacPadding( + padding: StacEdgeInsets.only(left: 10, right: 10), + child: StacColumn( + crossAxisAlignment: StacCrossAxisAlignment.center, + spacing: 52, + children: [ + StacSizedBox(height: 1), + StacLinearProgressIndicator(color: '#672BFF', minHeight: 3), + StacLinearProgressIndicator( + color: '#541204', + minHeight: 6, + backgroundColor: '#FFD700', + borderRadius: StacBorderRadius.only( + topLeft: 10, + topRight: 10, + bottomLeft: 10, + bottomRight: 10)), + StacLinearProgressIndicator( + color: '#bd3ed3', minHeight: 3, value: 0.5) + ]))); +} diff --git a/stac_playground/lib/dsl/list_tile.dart b/stac_playground/lib/dsl/list_tile.dart new file mode 100644 index 00000000..a174eafb --- /dev/null +++ b/stac_playground/lib/dsl/list_tile.dart @@ -0,0 +1,48 @@ +import 'package:stac_core/stac_core.dart'; + +@StacScreen(screenName: 'list_tile') +StacWidget listTileExample() { + return StacScaffold( + appBar: StacAppBar(title: StacText(data: 'Tiles')), + body: StacColumn( + mainAxisAlignment: StacMainAxisAlignment.start, + crossAxisAlignment: StacCrossAxisAlignment.center, + children: [ + StacSizedBox(height: 12), + StacListTile( + leading: StacIcon(icon: 'person'), + title: StacPadding( + padding: StacEdgeInsets.only(top: 10), + child: StacText( + data: 'Andrew Symonds', + style: StacTextStyle(fontSize: 18))), + subtitle: StacPadding( + padding: StacEdgeInsets.only(top: 10), + child: StacText( + data: + 'Andrew Symonds was an Australian international cricketer, who played all three formats as a batting all-rounder. Commonly nicknamed "Roy", he was a key member of two World Cup winning squads. Symonds played as a right-handed, middle order batsman and alternated between medium pace and off-spin', + style: StacTextStyle(fontSize: 14))), + trailing: StacIcon( + iconType: StacIconType.material, + icon: 'more_vert', + size: 24)), + StacSizedBox(height: 12), + StacListTile( + leading: StacIcon(icon: 'person'), + title: StacPadding( + padding: StacEdgeInsets.only(top: 10), + child: StacText( + data: 'Adam Gilchrist', + style: StacTextStyle(fontSize: 18))), + subtitle: StacPadding( + padding: StacEdgeInsets.only(top: 10), + child: StacText( + data: + 'Adam Craig Gilchrist is an Australian cricket commentator and former international cricketer and captain of the Australia national cricket team. He was an attacking left-handed batsman and record-breaking wicket-keeper', + style: StacTextStyle(fontSize: 14))), + trailing: StacIcon( + iconType: StacIconType.material, + icon: 'more_vert', + size: 24)) + ])); +} diff --git a/stac_playground/lib/dsl/list_view.dart b/stac_playground/lib/dsl/list_view.dart new file mode 100644 index 00000000..da02fba8 --- /dev/null +++ b/stac_playground/lib/dsl/list_view.dart @@ -0,0 +1,262 @@ +import 'package:stac_core/stac_core.dart'; + +@StacScreen(screenName: 'list_view') +StacWidget listViewExample() { + return StacScaffold( + appBar: StacAppBar(title: StacText(data: 'Listview')), + body: StacListView( + shrinkWrap: true, + separator: StacContainer(height: 10), + children: [ + StacListTile( + leading: StacContainer( + height: 50, + width: 50, + color: '#165FC7', + child: StacColumn( + mainAxisAlignment: StacMainAxisAlignment.center, + crossAxisAlignment: StacCrossAxisAlignment.center, + children: [ + StacText( + data: '1', style: StacTextStyle(fontSize: 21)) + ])), + title: StacPadding( + padding: StacEdgeInsets.only(top: 10), + child: StacText( + data: 'Item 1', style: StacTextStyle(fontSize: 18))), + subtitle: StacPadding( + padding: StacEdgeInsets.only(top: 10), + child: StacText( + data: 'Item description', + style: StacTextStyle(fontSize: 14))), + trailing: StacIcon( + iconType: StacIconType.material, + icon: 'more_vert', + size: 24)), + StacListTile( + leading: StacContainer( + height: 50, + width: 50, + color: '#165FC7', + child: StacColumn( + mainAxisAlignment: StacMainAxisAlignment.center, + crossAxisAlignment: StacCrossAxisAlignment.center, + children: [ + StacText( + data: '2', style: StacTextStyle(fontSize: 21)) + ])), + title: StacPadding( + padding: StacEdgeInsets.only(top: 10), + child: StacText( + data: 'Item 2', style: StacTextStyle(fontSize: 18))), + subtitle: StacPadding( + padding: StacEdgeInsets.only(top: 10), + child: StacText( + data: 'Item description', + style: StacTextStyle(fontSize: 14))), + trailing: StacIcon( + iconType: StacIconType.material, + icon: 'more_vert', + size: 24)), + StacListTile( + leading: StacContainer( + height: 50, + width: 50, + color: '#165FC7', + child: StacColumn( + mainAxisAlignment: StacMainAxisAlignment.center, + crossAxisAlignment: StacCrossAxisAlignment.center, + children: [ + StacText( + data: '3', style: StacTextStyle(fontSize: 21)) + ])), + title: StacPadding( + padding: StacEdgeInsets.only(top: 10), + child: StacText( + data: 'Item 3', style: StacTextStyle(fontSize: 18))), + subtitle: StacPadding( + padding: StacEdgeInsets.only(top: 10), + child: StacText( + data: 'Item description', + style: StacTextStyle(fontSize: 14))), + trailing: StacIcon( + iconType: StacIconType.material, + icon: 'more_vert', + size: 24)), + StacListTile( + leading: StacContainer( + height: 50, + width: 50, + color: '#165FC7', + child: StacColumn( + mainAxisAlignment: StacMainAxisAlignment.center, + crossAxisAlignment: StacCrossAxisAlignment.center, + children: [ + StacText( + data: '4', style: StacTextStyle(fontSize: 21)) + ])), + title: StacPadding( + padding: StacEdgeInsets.only(top: 10), + child: StacText( + data: 'Item 4', style: StacTextStyle(fontSize: 18))), + subtitle: StacPadding( + padding: StacEdgeInsets.only(top: 10), + child: StacText( + data: 'Item description', + style: StacTextStyle(fontSize: 14))), + trailing: StacIcon( + iconType: StacIconType.material, + icon: 'more_vert', + size: 24)), + StacListTile( + leading: StacContainer( + height: 50, + width: 50, + color: '#165FC7', + child: StacColumn( + mainAxisAlignment: StacMainAxisAlignment.center, + crossAxisAlignment: StacCrossAxisAlignment.center, + children: [ + StacText( + data: '5', style: StacTextStyle(fontSize: 21)) + ])), + title: StacPadding( + padding: StacEdgeInsets.only(top: 10), + child: StacText( + data: 'Item 5', style: StacTextStyle(fontSize: 18))), + subtitle: StacPadding( + padding: StacEdgeInsets.only(top: 10), + child: StacText( + data: 'Item description', + style: StacTextStyle(fontSize: 14))), + trailing: StacIcon( + iconType: StacIconType.material, + icon: 'more_vert', + size: 24)), + StacListTile( + leading: StacContainer( + height: 50, + width: 50, + color: '#165FC7', + child: StacColumn( + mainAxisAlignment: StacMainAxisAlignment.center, + crossAxisAlignment: StacCrossAxisAlignment.center, + children: [ + StacText( + data: '6', style: StacTextStyle(fontSize: 21)) + ])), + title: StacPadding( + padding: StacEdgeInsets.only(top: 10), + child: StacText( + data: 'Item 6', style: StacTextStyle(fontSize: 18))), + subtitle: StacPadding( + padding: StacEdgeInsets.only(top: 10), + child: StacText( + data: 'Item description', + style: StacTextStyle(fontSize: 14))), + trailing: StacIcon( + iconType: StacIconType.material, + icon: 'more_vert', + size: 24)), + StacListTile( + leading: StacContainer( + height: 50, + width: 50, + color: '#165FC7', + child: StacColumn( + mainAxisAlignment: StacMainAxisAlignment.center, + crossAxisAlignment: StacCrossAxisAlignment.center, + children: [ + StacText( + data: '7', style: StacTextStyle(fontSize: 21)) + ])), + title: StacPadding( + padding: StacEdgeInsets.only(top: 10), + child: StacText( + data: 'Item 7', style: StacTextStyle(fontSize: 18))), + subtitle: StacPadding( + padding: StacEdgeInsets.only(top: 10), + child: StacText( + data: 'Item description', + style: StacTextStyle(fontSize: 14))), + trailing: StacIcon( + iconType: StacIconType.material, + icon: 'more_vert', + size: 24)), + StacListTile( + leading: StacContainer( + height: 50, + width: 50, + color: '#165FC7', + child: StacColumn( + mainAxisAlignment: StacMainAxisAlignment.center, + crossAxisAlignment: StacCrossAxisAlignment.center, + children: [ + StacText( + data: '8', style: StacTextStyle(fontSize: 21)) + ])), + title: StacPadding( + padding: StacEdgeInsets.only(top: 10), + child: StacText( + data: 'Item 8', style: StacTextStyle(fontSize: 18))), + subtitle: StacPadding( + padding: StacEdgeInsets.only(top: 10), + child: StacText( + data: 'Item description', + style: StacTextStyle(fontSize: 14))), + trailing: StacIcon( + iconType: StacIconType.material, + icon: 'more_vert', + size: 24)), + StacListTile( + leading: StacContainer( + height: 50, + width: 50, + color: '#165FC7', + child: StacColumn( + mainAxisAlignment: StacMainAxisAlignment.center, + crossAxisAlignment: StacCrossAxisAlignment.center, + children: [ + StacText( + data: '9', style: StacTextStyle(fontSize: 21)) + ])), + title: StacPadding( + padding: StacEdgeInsets.only(top: 10), + child: StacText( + data: 'Item 9', style: StacTextStyle(fontSize: 18))), + subtitle: StacPadding( + padding: StacEdgeInsets.only(top: 10), + child: StacText( + data: 'Item description', + style: StacTextStyle(fontSize: 14))), + trailing: StacIcon( + iconType: StacIconType.material, + icon: 'more_vert', + size: 24)), + StacListTile( + leading: StacContainer( + height: 50, + width: 50, + color: '#165FC7', + child: StacColumn( + mainAxisAlignment: StacMainAxisAlignment.center, + crossAxisAlignment: StacCrossAxisAlignment.center, + children: [ + StacText( + data: '10', style: StacTextStyle(fontSize: 21)) + ])), + title: StacPadding( + padding: StacEdgeInsets.only(top: 10), + child: StacText( + data: 'Item 10', style: StacTextStyle(fontSize: 18))), + subtitle: StacPadding( + padding: StacEdgeInsets.only(top: 10), + child: StacText( + data: 'Item description', + style: StacTextStyle(fontSize: 14))), + trailing: StacIcon( + iconType: StacIconType.material, + icon: 'more_vert', + size: 24)) + ])); +} diff --git a/stac_playground/lib/dsl/navigation.dart b/stac_playground/lib/dsl/navigation.dart new file mode 100644 index 00000000..f0eec3a7 --- /dev/null +++ b/stac_playground/lib/dsl/navigation.dart @@ -0,0 +1,104 @@ +import 'package:stac_core/stac_core.dart'; + +@StacScreen(screenName: 'navigation') +StacWidget navigationExample() { + return StacScaffold( + appBar: StacAppBar(title: StacText(data: 'Navigation')), + body: StacRow( + mainAxisAlignment: StacMainAxisAlignment.center, + crossAxisAlignment: StacCrossAxisAlignment.center, + children: [ + StacColumn( + mainAxisAlignment: StacMainAxisAlignment.center, + crossAxisAlignment: StacCrossAxisAlignment.center, + children: [ + StacElevatedButton( + style: _buttonStyle(), + onPressed: StacNavigateAction( + navigationStyle: NavigationStyle.push, + widgetJson: { + 'type': 'exampleScreen', + 'assetPath': 'assets/json/navigation_example.json', + }, + ), + child: StacText(data: 'Push'), + ), + StacSizedBox(height: 8), + StacElevatedButton( + style: _buttonStyle(), + onPressed: StacNavigateAction( + navigationStyle: NavigationStyle.pop, + ), + child: StacText(data: 'Pop'), + ), + StacSizedBox(height: 8), + StacElevatedButton( + style: _buttonStyle(), + onPressed: StacNavigateAction( + navigationStyle: NavigationStyle.pushReplacement, + widgetJson: { + 'type': 'exampleScreen', + 'assetPath': 'assets/json/navigation_example.json', + }, + ), + child: StacText(data: 'Push and Replace'), + ), + StacSizedBox(height: 8), + StacElevatedButton( + style: _buttonStyle(), + onPressed: StacNavigateAction( + navigationStyle: NavigationStyle.pushAndRemoveAll, + widgetJson: { + 'type': 'exampleScreen', + 'assetPath': 'assets/json/navigation_example.json', + }, + ), + child: StacText(data: 'Push and Remove All'), + ), + StacSizedBox(height: 8), + StacElevatedButton( + style: _buttonStyle(), + onPressed: StacNavigateAction( + navigationStyle: NavigationStyle.popAll, + ), + child: StacText(data: 'Pop All'), + ), + StacSizedBox(height: 8), + StacElevatedButton( + style: _buttonStyle(), + onPressed: StacNavigateAction( + navigationStyle: NavigationStyle.pushReplacementNamed, + routeName: '/detailsScreen', + ), + child: StacText(data: 'Push Named and Replace'), + ), + StacSizedBox(height: 8), + StacElevatedButton( + style: _buttonStyle(), + onPressed: StacNavigateAction( + navigationStyle: NavigationStyle.pushNamed, + routeName: '/detailsScreen', + ), + child: StacText(data: 'Push Named'), + ), + StacSizedBox(height: 8), + StacElevatedButton( + style: _buttonStyle(), + onPressed: StacNavigateAction( + navigationStyle: NavigationStyle.pushNamedAndRemoveAll, + routeName: '/homeScreen', + ), + child: StacText(data: 'Push Named and Remove Until'), + ), + ], + ), + ], + ), + ); +} + +StacButtonStyle _buttonStyle() { + return StacButtonStyle( + padding: StacEdgeInsets.only(top: 8, left: 12, right: 12, bottom: 8), + ); +} diff --git a/stac_playground/lib/dsl/navigation_bar.dart b/stac_playground/lib/dsl/navigation_bar.dart new file mode 100644 index 00000000..bb1ef62b --- /dev/null +++ b/stac_playground/lib/dsl/navigation_bar.dart @@ -0,0 +1,56 @@ +import 'package:stac_core/stac_core.dart'; + +@StacScreen(screenName: 'navigation_bar') +StacWidget navigationBarExample() { + return StacDefaultNavigationController( + length: 3, + child: StacScaffold( + appBar: StacAppBar(title: StacText(data: 'Navigation Bar Screen')), + body: StacNavigationView( + children: [ + StacCenter( + child: StacText(data: 'Home', style: StacTextStyle(fontSize: 24)), + ), + StacCenter( + child: StacText(data: 'Search', style: StacTextStyle(fontSize: 24)), + ), + StacCenter( + child: + StacText(data: 'Profile', style: StacTextStyle(fontSize: 24)), + ), + ], + ), + bottomNavigationBar: StacNavigationBar( + labelBehavior: StacNavigationDestinationLabelBehavior.alwaysShow, + destinations: [ + StacNavigationDestination( + label: 'Home', + icon: StacIcon( + iconType: StacIconType.material, + icon: 'home_outlined', + ), + selectedIcon: StacIcon( + iconType: StacIconType.material, + icon: 'home', + ), + ), + StacNavigationDestination( + label: 'Search', + icon: StacIcon(iconType: StacIconType.material, icon: 'search'), + ), + StacNavigationDestination( + label: 'Profile', + icon: StacIcon( + iconType: StacIconType.material, + icon: 'account_circle_outlined', + ), + selectedIcon: StacIcon( + iconType: StacIconType.material, + icon: 'account_circle', + ), + ), + ], + ), + ), + ); +} diff --git a/stac_playground/lib/dsl/opacity.dart b/stac_playground/lib/dsl/opacity.dart new file mode 100644 index 00000000..b1cc44f9 --- /dev/null +++ b/stac_playground/lib/dsl/opacity.dart @@ -0,0 +1,14 @@ +import 'package:stac_core/stac_core.dart'; + +@StacScreen(screenName: 'opacity') +StacWidget opacityExample() { + return StacScaffold( + appBar: StacAppBar(title: StacText(data: 'Opacity')), + body: StacCenter( + child: StacOpacity( + opacity: 0.5, + child: StacText( + data: 'Opacity Widget', + style: StacTextStyle( + fontSize: 23, fontWeight: StacFontWeight.w600))))); +} diff --git a/stac_playground/lib/dsl/outlined_button.dart b/stac_playground/lib/dsl/outlined_button.dart new file mode 100644 index 00000000..1f98b6b3 --- /dev/null +++ b/stac_playground/lib/dsl/outlined_button.dart @@ -0,0 +1,66 @@ +import 'package:stac_core/stac_core.dart'; + +@StacScreen(screenName: 'outlined_button') +StacWidget outlinedButtonExample() { + return StacScaffold( + appBar: StacAppBar(title: StacText(data: 'Outlined Button')), + body: StacRow( + mainAxisAlignment: StacMainAxisAlignment.center, + crossAxisAlignment: StacCrossAxisAlignment.center, + children: [ + StacColumn( + mainAxisAlignment: StacMainAxisAlignment.center, + crossAxisAlignment: StacCrossAxisAlignment.center, + children: [ + StacRow( + mainAxisAlignment: StacMainAxisAlignment.center, + crossAxisAlignment: StacCrossAxisAlignment.center, + children: [ + StacOutlinedButton( + child: StacText(data: 'BUTTON'), + style: StacButtonStyle( + padding: StacEdgeInsets.only( + top: 8, left: 12, right: 12, bottom: 8)), + onPressed: StacAction()), + StacSizedBox(width: 20), + StacOutlinedButton( + child: StacRow(children: [ + StacIcon( + iconType: StacIconType.material, + icon: 'add', + size: 18), + StacSizedBox(width: 4), + StacText(data: 'BUTTON') + ]), + style: StacButtonStyle( + padding: StacEdgeInsets.only( + top: 8, left: 12, right: 12, bottom: 8)), + onPressed: StacAction()) + ]), + StacSizedBox(height: 12), + StacRow( + mainAxisAlignment: StacMainAxisAlignment.center, + crossAxisAlignment: StacCrossAxisAlignment.center, + children: [ + StacOutlinedButton( + child: StacText(data: 'BUTTON'), + style: StacButtonStyle( + padding: StacEdgeInsets.only( + top: 8, left: 12, right: 12, bottom: 8))), + StacSizedBox(width: 20), + StacOutlinedButton( + child: StacRow(children: [ + StacIcon( + iconType: StacIconType.material, + icon: 'add', + size: 18), + StacSizedBox(width: 4), + StacText(data: 'BUTTON') + ]), + style: StacButtonStyle( + padding: StacEdgeInsets.only( + top: 8, left: 12, right: 12, bottom: 8))) + ]) + ]) + ])); +} diff --git a/stac_playground/lib/dsl/padding.dart b/stac_playground/lib/dsl/padding.dart new file mode 100644 index 00000000..b9142b2e --- /dev/null +++ b/stac_playground/lib/dsl/padding.dart @@ -0,0 +1,46 @@ +import 'package:stac_core/stac_core.dart'; + +@StacScreen(screenName: 'padding') +StacWidget paddingExample() { + return StacScaffold( + appBar: StacAppBar(title: StacText(data: 'Padding')), + body: StacSingleChildScrollView( + child: StacContainer( + padding: + StacEdgeInsets.only(left: 12, right: 12, top: 12, bottom: 12), + child: StacColumn( + mainAxisAlignment: StacMainAxisAlignment.center, + crossAxisAlignment: StacCrossAxisAlignment.center, + children: [ + StacSizedBox(height: 24), + StacPadding( + padding: StacEdgeInsets.only(left: 0, right: 0), + child: StacContainer( + color: '#672BFF', height: 75, width: 700)), + StacSizedBox(height: 24), + StacPadding( + padding: StacEdgeInsets.only(left: 24, right: 24), + child: StacContainer( + color: '#FC5632', height: 75, width: 700)), + StacSizedBox(height: 24), + StacPadding( + padding: StacEdgeInsets.only(left: 48, right: 48), + child: StacContainer( + color: '#D9D9D9', height: 75, width: 700)), + StacSizedBox(height: 24), + StacPadding( + padding: StacEdgeInsets.only(left: 96, right: 96), + child: StacContainer( + color: '#672BFF', height: 75, width: 700)), + StacSizedBox(height: 24), + StacPadding( + padding: StacEdgeInsets.only(left: 120, right: 120), + child: StacContainer( + color: '#FC5632', height: 75, width: 700)), + StacSizedBox(height: 24), + StacPadding( + padding: StacEdgeInsets.only(left: 144, right: 144), + child: StacContainer( + color: '#D9D9D9', height: 75, width: 700)) + ])))); +} diff --git a/stac_playground/lib/dsl/page_view.dart b/stac_playground/lib/dsl/page_view.dart new file mode 100644 index 00000000..9dea3f82 --- /dev/null +++ b/stac_playground/lib/dsl/page_view.dart @@ -0,0 +1,30 @@ +import 'package:stac_core/stac_core.dart'; + +@StacScreen(screenName: 'page_view') +StacWidget pageViewExample() { + return StacScaffold( + appBar: StacAppBar(title: StacText(data: 'PageView')), + body: StacPageView(children: [ + StacContainer( + color: '#D9D9D9', + child: StacCenter( + child: StacText( + data: 'Page 1', + style: StacTextStyle( + fontSize: 23, fontWeight: StacFontWeight.w400)))), + StacContainer( + color: '#FC3F1B', + child: StacCenter( + child: StacText( + data: 'Page 2', + style: StacTextStyle( + fontSize: 23, fontWeight: StacFontWeight.w400)))), + StacContainer( + color: '#D9D9D9', + child: StacCenter( + child: StacText( + data: 'Page 3', + style: StacTextStyle( + fontSize: 23, fontWeight: StacFontWeight.w400)))) + ])); +} diff --git a/stac_playground/lib/dsl/placeholder.dart b/stac_playground/lib/dsl/placeholder.dart new file mode 100644 index 00000000..ecc5ac19 --- /dev/null +++ b/stac_playground/lib/dsl/placeholder.dart @@ -0,0 +1,17 @@ +import 'package:stac_core/stac_core.dart'; + +@StacScreen(screenName: 'placeholder') +StacWidget placeholderExample() { + return StacScaffold( + appBar: StacAppBar(title: StacText(data: 'Placeholder')), + body: StacListView(shrinkWrap: true, children: [ + StacSizedBox(height: 25), + StacPlaceholder( + color: '#455A64', + strokeWidth: 2, + fallbackHeight: 400, + fallbackWidth: 400), + StacSizedBox(height: 25), + StacPlaceholder(color: '#672BFF', strokeWidth: 5, fallbackHeight: 100) + ])); +} diff --git a/stac_playground/lib/dsl/radio.dart b/stac_playground/lib/dsl/radio.dart new file mode 100644 index 00000000..3726c18b --- /dev/null +++ b/stac_playground/lib/dsl/radio.dart @@ -0,0 +1,46 @@ +import 'package:stac_core/stac_core.dart'; + +@StacScreen(screenName: 'radio') +StacWidget radioExample() { + return StacScaffold( + appBar: StacAppBar(title: StacText(data: 'Stac Radio')), + body: StacForm( + child: StacRadioGroup( + child: StacColumn( + children: [ + StacListTile( + leading: StacRadio( + radioType: StacRadioType.adaptive, + value: '1', + ), + title: StacText( + data: 'Male', + style: StacTextStyle(fontSize: 21), + ), + ), + StacListTile( + leading: StacRadio( + radioType: StacRadioType.adaptive, + value: '2', + ), + title: StacText( + data: 'Female', + style: StacTextStyle(fontSize: 21), + ), + ), + StacListTile( + leading: StacRadio( + radioType: StacRadioType.adaptive, + value: '3', + ), + title: StacText( + data: 'Other', + style: StacTextStyle(fontSize: 21), + ), + ), + ], + ), + ), + ), + ); +} diff --git a/stac_playground/lib/dsl/refresh_indicator.dart b/stac_playground/lib/dsl/refresh_indicator.dart new file mode 100644 index 00000000..0e7513bf --- /dev/null +++ b/stac_playground/lib/dsl/refresh_indicator.dart @@ -0,0 +1,22 @@ +import 'package:stac_core/stac_core.dart'; + +@StacScreen(screenName: 'refresh_indicator') +StacWidget refreshIndicatorExample() { + return StacScaffold( + body: StacRefreshIndicator( + onRefresh: StacNetworkRequest( + url: + 'https://raw.githubusercontent.com/StacDev/stac/main/stac_playground/assets/json/list_view_example.json', + method: Method.get, + contentType: 'application/json', + ), + child: StacNetworkWidget( + request: StacNetworkRequest( + url: + 'https://raw.githubusercontent.com/StacDev/stac/main/stac_playground/assets/json/list_view_example.json', + method: Method.get, + ), + ), + ), + ); +} diff --git a/stac_playground/lib/dsl/row.dart b/stac_playground/lib/dsl/row.dart new file mode 100644 index 00000000..b281522d --- /dev/null +++ b/stac_playground/lib/dsl/row.dart @@ -0,0 +1,106 @@ +import 'package:stac_core/stac_core.dart'; + +@StacScreen(screenName: 'row') +StacWidget rowExample() { + return StacScaffold( + appBar: StacAppBar(title: StacText(data: 'Row')), + body: StacSingleChildScrollView( + child: StacRow( + mainAxisAlignment: StacMainAxisAlignment.center, + crossAxisAlignment: StacCrossAxisAlignment.center, + children: [ + StacColumn( + mainAxisAlignment: StacMainAxisAlignment.center, + crossAxisAlignment: StacCrossAxisAlignment.center, + children: [ + StacSizedBox(height: 24), + StacRow( + mainAxisAlignment: StacMainAxisAlignment.center, + crossAxisAlignment: StacCrossAxisAlignment.center, + spacing: 12, + children: [ + StacImage( + src: + 'https://images.pexels.com/photos/2718416/pexels-photo-2718416.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=2', + width: 100), + StacImage( + src: + 'https://images.pexels.com/photos/121629/pexels-photo-121629.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=2', + width: 100), + StacImage( + src: + 'https://images.pexels.com/photos/1414642/pexels-photo-1414642.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=2', + width: 100) + ]), + StacSizedBox(height: 32), + StacRow( + mainAxisAlignment: StacMainAxisAlignment.center, + crossAxisAlignment: StacCrossAxisAlignment.center, + children: [ + StacOutlinedButton( + child: StacRow(children: [ + StacIcon( + iconType: StacIconType.material, + icon: 'add', + size: 18), + StacSizedBox(width: 4), + StacText(data: 'BUTTON') + ]), + style: StacButtonStyle( + padding: StacEdgeInsets.only( + top: 8, left: 12, right: 12, bottom: 8)), + onPressed: StacAction()), + StacSizedBox(width: 12), + StacElevatedButton( + child: StacText(data: 'BUTTON'), + style: StacButtonStyle( + padding: StacEdgeInsets.only( + top: 8, left: 12, right: 12, bottom: 8)), + onPressed: StacAction()), + StacSizedBox(width: 12), + StacOutlinedButton( + child: StacRow(children: [ + StacText(data: 'BUTTON'), + StacSizedBox(width: 4), + StacIcon( + iconType: StacIconType.material, + icon: 'remove', + size: 18) + ]), + style: StacButtonStyle( + padding: StacEdgeInsets.only( + top: 8, left: 12, right: 12, bottom: 8)), + onPressed: StacAction()) + ]), + StacSizedBox(height: 32), + StacRow( + mainAxisAlignment: StacMainAxisAlignment.center, + crossAxisAlignment: StacCrossAxisAlignment.center, + children: [ + StacFloatingActionButton( + child: StacIcon( + iconType: StacIconType.material, + icon: 'add', + size: 32), + onPressed: StacAction()), + StacSizedBox(width: 12), + StacFloatingActionButton( + buttonType: StacFloatingActionButtonType.large, + child: StacIcon( + iconType: StacIconType.material, + icon: 'add', + size: 32), + onPressed: StacAction()), + StacSizedBox(width: 12), + StacFloatingActionButton( + buttonType: StacFloatingActionButtonType.extended, + icon: StacIcon( + iconType: StacIconType.material, + icon: 'add', + size: 32), + child: StacText(data: 'Create'), + onPressed: StacAction()) + ]) + ]) + ]))); +} diff --git a/stac_playground/lib/dsl/scaffold.dart b/stac_playground/lib/dsl/scaffold.dart new file mode 100644 index 00000000..8e9737a1 --- /dev/null +++ b/stac_playground/lib/dsl/scaffold.dart @@ -0,0 +1,42 @@ +import 'package:stac_core/stac_core.dart'; + +@StacScreen(screenName: 'scaffold') +StacWidget scaffoldExample() { + return StacScaffold( + appBar: StacAppBar( + title: StacText(data: 'Scaffold'), + leading: StacIconButton( + icon: StacIcon(iconType: StacIconType.material, icon: 'menu'), + onPressed: StacAction()), + actions: [ + StacIconButton( + icon: StacIcon( + iconType: StacIconType.cupertino, icon: 'heart_solid'), + onPressed: StacAction()), + StacIconButton( + icon: StacIcon(iconType: StacIconType.material, icon: 'search'), + onPressed: StacAction()), + StacIconButton( + icon: StacIcon( + iconType: StacIconType.material, icon: 'more_horiz'), + onPressed: StacAction()) + ]), + body: StacRow( + mainAxisAlignment: StacMainAxisAlignment.center, + crossAxisAlignment: StacCrossAxisAlignment.center, + children: [ + StacColumn( + mainAxisAlignment: StacMainAxisAlignment.center, + crossAxisAlignment: StacCrossAxisAlignment.center, + children: [ + StacText(data: 'Home', style: StacTextStyle(fontSize: 17)) + ]) + ]), + floatingActionButton: StacFloatingActionButton( + backgroundColor: '#FC3F1B', + foregroundColor: '#ffffff', + buttonType: StacFloatingActionButtonType.medium, + child: + StacIcon(iconType: StacIconType.material, icon: 'add', size: 32), + onPressed: StacAction())); +} diff --git a/stac_playground/lib/dsl/scroll_view.dart b/stac_playground/lib/dsl/scroll_view.dart new file mode 100644 index 00000000..b27a19d8 --- /dev/null +++ b/stac_playground/lib/dsl/scroll_view.dart @@ -0,0 +1,15 @@ +import 'package:stac_core/stac_core.dart'; + +@StacScreen(screenName: 'scroll_view') +StacWidget scrollViewExample() { + return StacScaffold( + appBar: StacAppBar(title: StacText(data: 'Scrollview')), + body: StacListView(shrinkWrap: true, children: [ + StacSizedBox(height: 52), + StacContainer(color: '#672BFF', height: 400, width: 200), + StacSizedBox(height: 52), + StacContainer(color: '#FC5632', height: 400, width: 200), + StacSizedBox(height: 52), + StacContainer(color: '#D9D9D9', height: 400, width: 200) + ])); +} diff --git a/stac_playground/lib/dsl/selectable_text.dart b/stac_playground/lib/dsl/selectable_text.dart new file mode 100644 index 00000000..596b6d77 --- /dev/null +++ b/stac_playground/lib/dsl/selectable_text.dart @@ -0,0 +1,70 @@ +import 'package:stac_core/stac_core.dart'; + +@StacScreen(screenName: 'selectable_text') +StacWidget selectableTextExample() { + return StacScaffold( + appBar: StacAppBar(title: StacText(data: 'Selectable Text')), + body: StacSingleChildScrollView( + child: StacPadding( + padding: StacEdgeInsets.only(top: 12, left: 12, right: 12, bottom: 12), + child: StacColumn( + crossAxisAlignment: StacCrossAxisAlignment.start, + children: [ + StacText( + data: 'Standard Selectable Text', + style: + StacTextStyle(fontSize: 18, fontWeight: StacFontWeight.w600), + ), + StacSizedBox(height: 10), + StacSelectableText( + data: + 'You can select this text. Long press or double tap to select.', + ), + StacSizedBox(height: 32), + StacText( + data: 'Rich Selectable Text', + style: + StacTextStyle(fontSize: 18, fontWeight: StacFontWeight.w600), + ), + StacSizedBox(height: 10), + StacSelectableText( + data: 'This is a ', + children: [ + StacTextSpan( + text: 'selectable rich text.', + style: StacTextStyle( + fontWeight: StacFontWeight.w800, color: '#6700A4'), + ), + ], + ), + StacSizedBox(height: 32), + StacText( + data: 'Custom Cursor Selectable Text', + style: + StacTextStyle(fontSize: 18, fontWeight: StacFontWeight.w600), + ), + StacSizedBox(height: 10), + StacSelectableText( + data: 'This text has a red cursor.', + showCursor: true, + cursorColor: '#FF0000', + cursorWidth: 5.0, + ), + StacSizedBox(height: 32), + StacText( + data: 'Interactive Selection Disabled', + style: + StacTextStyle(fontSize: 18, fontWeight: StacFontWeight.w600), + ), + StacSizedBox(height: 10), + StacSelectableText( + data: + 'You cannot select this text (interactive selection disabled).', + enableInteractiveSelection: false, + ), + ], + ), + ), + ), + ); +} diff --git a/stac_playground/lib/dsl/sized_box.dart b/stac_playground/lib/dsl/sized_box.dart new file mode 100644 index 00000000..c39a129a --- /dev/null +++ b/stac_playground/lib/dsl/sized_box.dart @@ -0,0 +1,21 @@ +import 'package:stac_core/stac_core.dart'; + +@StacScreen(screenName: 'sized_box') +StacWidget sizedBoxExample() { + return StacScaffold( + appBar: StacAppBar(title: StacText(data: 'Sizedbox')), + body: StacListView(shrinkWrap: true, children: [ + StacSizedBox(height: 25), + StacContainer(color: '#672BFF', height: 75), + StacSizedBox(height: 50), + StacContainer(color: '#FC5632', height: 75), + StacSizedBox(height: 75), + StacContainer(color: '#D9D9D9', height: 75), + StacSizedBox(height: 100), + StacContainer(color: '#672BFF', height: 75), + StacSizedBox(height: 125), + StacContainer(color: '#FC5632', height: 75), + StacSizedBox(height: 150), + StacContainer(color: '#D9D9D9', height: 75) + ])); +} diff --git a/stac_playground/lib/dsl/slider.dart b/stac_playground/lib/dsl/slider.dart new file mode 100644 index 00000000..3b830a33 --- /dev/null +++ b/stac_playground/lib/dsl/slider.dart @@ -0,0 +1,19 @@ +import 'package:stac_core/stac_core.dart'; + +@StacScreen(screenName: 'slider') +StacWidget sliderExample() { + return StacScaffold( + appBar: StacAppBar(title: StacText(data: 'Stac Slider')), + body: StacForm( + child: StacCenter( + child: StacSlider( + id: 'example_slider', + sliderType: StacSliderType.material, + value: 20, + max: 100, + divisions: 5, + ), + ), + ), + ); +} diff --git a/stac_playground/lib/dsl/sliver_app_bar.dart b/stac_playground/lib/dsl/sliver_app_bar.dart new file mode 100644 index 00000000..af32c431 --- /dev/null +++ b/stac_playground/lib/dsl/sliver_app_bar.dart @@ -0,0 +1,27 @@ +import 'package:stac_core/stac_core.dart'; + +@StacScreen(screenName: 'sliver_app_bar') +StacWidget sliverAppBarExample() { + return StacScaffold( + body: StacCustomScrollView(slivers: [ + StacSliverAppBar( + title: StacText(data: 'SliverAppBar'), + leading: StacIconButton( + icon: StacIcon(iconType: StacIconType.material, icon: 'menu'), + onPressed: StacAction()), + backgroundColor: 'primary', + actions: [ + StacIconButton( + icon: StacIcon( + iconType: StacIconType.cupertino, icon: 'heart_solid'), + onPressed: StacAction()), + StacIconButton( + icon: StacIcon(iconType: StacIconType.material, icon: 'search'), + onPressed: StacAction()), + StacIconButton( + icon: + StacIcon(iconType: StacIconType.material, icon: 'more_horiz'), + onPressed: StacAction()) + ]) + ])); +} diff --git a/stac_playground/lib/dsl/sliver_fill_remaining.dart b/stac_playground/lib/dsl/sliver_fill_remaining.dart new file mode 100644 index 00000000..c5353494 --- /dev/null +++ b/stac_playground/lib/dsl/sliver_fill_remaining.dart @@ -0,0 +1,12 @@ +import 'package:stac_core/stac_core.dart'; + +@StacScreen(screenName: 'sliver_fill_remaining') +StacWidget sliverFillRemainingExample() { + return StacScaffold( + body: StacCustomScrollView(slivers: [ + StacSliverFillRemaining( + hasScrollBody: false, + child: + StacCenter(child: StacText(data: 'This fills the remaining space'))) + ])); +} diff --git a/stac_playground/lib/dsl/sliver_grid.dart b/stac_playground/lib/dsl/sliver_grid.dart new file mode 100644 index 00000000..9e641da7 --- /dev/null +++ b/stac_playground/lib/dsl/sliver_grid.dart @@ -0,0 +1,43 @@ +import 'package:stac_core/stac_core.dart'; + +@StacScreen(screenName: 'sliver_grid') +StacWidget sliverGridExample() { + return StacScaffold( + body: StacCustomScrollView(slivers: [ + StacSliverGrid( + crossAxisCount: 2, + mainAxisSpacing: 16, + crossAxisSpacing: 16, + childAspectRatio: 1, + children: [ + StacContainer( + color: '#4CAF50', + child: StacCenter( + child: StacText( + data: 'Grid Item 1', + style: StacTextStyle( + color: '#FFFFFF', fontWeight: StacFontWeight.bold)))), + StacContainer( + color: '#4CAF50', + child: StacCenter( + child: StacText( + data: 'Grid Item 2', + style: StacTextStyle( + color: '#FFFFFF', fontWeight: StacFontWeight.bold)))), + StacContainer( + color: '#4CAF50', + child: StacCenter( + child: StacText( + data: 'Grid Item 3', + style: StacTextStyle( + color: '#FFFFFF', fontWeight: StacFontWeight.bold)))), + StacContainer( + color: '#4CAF50', + child: StacCenter( + child: StacText( + data: 'Grid Item 4', + style: StacTextStyle( + color: '#FFFFFF', fontWeight: StacFontWeight.bold)))) + ]) + ])); +} diff --git a/stac_playground/lib/dsl/sliver_list.dart b/stac_playground/lib/dsl/sliver_list.dart new file mode 100644 index 00000000..8c5d460c --- /dev/null +++ b/stac_playground/lib/dsl/sliver_list.dart @@ -0,0 +1,18 @@ +import 'package:stac_core/stac_core.dart'; + +@StacScreen(screenName: 'sliver_list') +StacWidget sliverListExample() { + return StacScaffold( + body: StacCustomScrollView(slivers: [ + StacSliverList(children: [ + StacContainer( + height: 80, + color: 'primary', + child: StacCenter(child: StacText(data: 'List Item 1'))), + StacContainer( + height: 80, + color: 'secondary', + child: StacCenter(child: StacText(data: 'List Item 2'))) + ]) + ])); +} diff --git a/stac_playground/lib/dsl/sliver_opacity.dart b/stac_playground/lib/dsl/sliver_opacity.dart new file mode 100644 index 00000000..f482d0e4 --- /dev/null +++ b/stac_playground/lib/dsl/sliver_opacity.dart @@ -0,0 +1,17 @@ +import 'package:stac_core/stac_core.dart'; + +@StacScreen(screenName: 'sliver_opacity') +StacWidget sliverOpacityExample() { + return StacScaffold( + body: StacCustomScrollView(slivers: [ + StacSliverOpacity( + opacity: 0.4, + sliver: StacSliverToBoxAdapter( + child: StacContainer( + height: 200, + color: 'secondary', + child: StacCenter( + child: StacText( + data: 'This sliver is faded using SliverOpacity'))))) + ])); +} diff --git a/stac_playground/lib/dsl/sliver_padding.dart b/stac_playground/lib/dsl/sliver_padding.dart new file mode 100644 index 00000000..9d8195b0 --- /dev/null +++ b/stac_playground/lib/dsl/sliver_padding.dart @@ -0,0 +1,29 @@ +import 'package:stac_core/stac_core.dart'; + +@StacScreen(screenName: 'sliver_padding') +StacWidget sliverPaddingExample() { + return StacScaffold( + body: StacCustomScrollView( + slivers: [ + StacSliverPadding( + padding: StacEdgeInsets.all(16.0), + sliver: StacSliverToBoxAdapter( + child: StacContainer( + height: 150, + color: '#4CAF50', + child: StacCenter( + child: StacText( + data: 'I am a Box inside a SliverPadding!', + style: StacTextStyle( + color: '#FFFFFF', + fontWeight: StacFontWeight.bold, + ), + ), + ), + ), + ), + ), + ], + ), + ); +} diff --git a/stac_playground/lib/dsl/sliver_safe_area.dart b/stac_playground/lib/dsl/sliver_safe_area.dart new file mode 100644 index 00000000..7b12758e --- /dev/null +++ b/stac_playground/lib/dsl/sliver_safe_area.dart @@ -0,0 +1,15 @@ +import 'package:stac_core/stac_core.dart'; + +@StacScreen(screenName: 'sliver_safe_area') +StacWidget sliverSafeAreaExample() { + return StacScaffold( + body: StacCustomScrollView(slivers: [ + StacSliverSafeArea( + top: true, + bottom: true, + sliver: StacSliverToBoxAdapter( + child: StacContainer( + padding: StacEdgeInsets.all(16), + child: StacText(data: 'Content inside SliverSafeArea')))) + ])); +} diff --git a/stac_playground/lib/dsl/sliver_to_box_adapter.dart b/stac_playground/lib/dsl/sliver_to_box_adapter.dart new file mode 100644 index 00000000..abc6cd98 --- /dev/null +++ b/stac_playground/lib/dsl/sliver_to_box_adapter.dart @@ -0,0 +1,29 @@ +import 'package:stac_core/stac_core.dart'; + +@StacScreen(screenName: 'sliver_to_box_adapter') +StacWidget sliverToBoxAdapterExample() { + return StacScaffold( + body: StacCustomScrollView( + slivers: [ + StacSliverPadding( + padding: StacEdgeInsets.all(16.0), + sliver: StacSliverToBoxAdapter( + child: StacContainer( + height: 150, + color: '#4CAF50', + child: StacCenter( + child: StacText( + data: 'I am a Box inside a SliverToBoxAdapter!', + style: StacTextStyle( + color: '#FFFFFF', + fontWeight: StacFontWeight.bold, + ), + ), + ), + ), + ), + ), + ], + ), + ); +} diff --git a/stac_playground/lib/dsl/sliver_visibility.dart b/stac_playground/lib/dsl/sliver_visibility.dart new file mode 100644 index 00000000..d25a1ded --- /dev/null +++ b/stac_playground/lib/dsl/sliver_visibility.dart @@ -0,0 +1,18 @@ +import 'package:stac_core/stac_core.dart'; + +@StacScreen(screenName: 'sliver_visibility') +StacWidget sliverVisibilityExample() { + return StacScaffold( + appBar: StacAppBar(title: StacText(data: 'Sliver Visibility')), + body: StacCustomScrollView(slivers: [ + StacSliverVisibility( + visible: true, + sliver: StacSliverToBoxAdapter( + child: StacContainer( + height: 200, + color: 'secondary', + child: StacCenter( + child: StacText( + data: 'This sliver is conditionally visible'))))) + ])); +} diff --git a/stac_playground/lib/dsl/snackbar.dart b/stac_playground/lib/dsl/snackbar.dart new file mode 100644 index 00000000..61e13b7f --- /dev/null +++ b/stac_playground/lib/dsl/snackbar.dart @@ -0,0 +1,17 @@ +import 'package:stac_core/stac_core.dart'; + +@StacScreen(screenName: 'snackbar') +StacWidget snackbarExample() { + return StacScaffold( + appBar: StacAppBar(title: StacText(data: 'SnackBar')), + body: StacCenter( + child: StacElevatedButton( + onPressed: StacSnackBar( + content: StacText(data: 'This is a Snackbar').toJson(), + behavior: StacSnackBarBehavior.floating, + ), + child: StacText(data: 'Show SnackBar'), + ), + ), + ); +} diff --git a/stac_playground/lib/dsl/spacer.dart b/stac_playground/lib/dsl/spacer.dart new file mode 100644 index 00000000..520d0511 --- /dev/null +++ b/stac_playground/lib/dsl/spacer.dart @@ -0,0 +1,35 @@ +import 'package:stac_core/stac_core.dart'; + +@StacScreen(screenName: 'spacer') +StacWidget spacerExample() { + return StacScaffold( + appBar: StacAppBar(title: StacText(data: 'Spacer')), + body: StacPadding( + padding: + StacEdgeInsets.only(left: 20, right: 20, top: 20, bottom: 20), + child: StacColumn(children: [ + StacTextField( + keyboardType: StacTextInputType.text, + maxLines: 1, + decoration: StacInputDecoration(hintText: 'Enter your pin')), + StacSpacer(), + StacRow(children: [ + StacRow(children: [ + StacText( + data: 'Forgot Pin', style: StacTextStyle(fontSize: 17)), + StacIcon( + iconType: StacIconType.material, + icon: 'keyboard_arrow_right', + size: 24) + ]), + StacSpacer(), + StacText(data: 'Need help?', style: StacTextStyle(fontSize: 17)) + ]), + StacSpacer(flex: 2), + StacElevatedButton( + child: StacText(data: 'Submit'), + style: StacButtonStyle( + backgroundColor: 'primary', foregroundColor: '#ffffff'), + onPressed: StacAction()) + ]))); +} diff --git a/stac_playground/lib/dsl/stack.dart b/stac_playground/lib/dsl/stack.dart new file mode 100644 index 00000000..c0a100fa --- /dev/null +++ b/stac_playground/lib/dsl/stack.dart @@ -0,0 +1,48 @@ +import 'package:stac_core/stac_core.dart'; + +@StacScreen(screenName: 'stack') +StacWidget stackExample() { + return StacScaffold( + appBar: StacAppBar(title: StacText(data: 'Stack')), + body: StacStack( + alignment: StacAlignment.center, + clipBehavior: StacClip.antiAlias, + children: [ + StacPositioned( + top: 30, + left: 30, + height: 150, + width: 150, + child: StacContainer( + width: 75, + height: 75, + color: '#81C784', + child: StacText( + data: 'Green', + style: StacTextStyle(fontSize: 20, color: '#FFFFFF')))), + StacPositioned( + top: 70, + left: 60, + height: 150, + width: 150, + child: StacContainer( + width: 75, + height: 75, + color: '#EF5350', + child: StacText( + data: 'Red', + style: StacTextStyle(fontSize: 20, color: '#FFFFFF')))), + StacPositioned( + top: 130, + left: 90, + height: 150, + width: 150, + child: StacContainer( + width: 75, + height: 75, + color: '#BA68C8', + child: StacText( + data: 'Purple', + style: StacTextStyle(fontSize: 20, color: '#FFFFFF')))) + ])); +} diff --git a/stac_playground/lib/dsl/switch.dart b/stac_playground/lib/dsl/switch.dart new file mode 100644 index 00000000..b6d99008 --- /dev/null +++ b/stac_playground/lib/dsl/switch.dart @@ -0,0 +1,32 @@ +import 'package:stac_core/stac_core.dart'; + +@StacScreen(screenName: 'switch') +StacWidget switchExample() { + return StacScaffold( + appBar: StacAppBar(title: StacText(data: 'Stac Switch')), + body: StacRow( + mainAxisAlignment: StacMainAxisAlignment.center, + crossAxisAlignment: StacCrossAxisAlignment.center, + children: [ + StacColumn( + mainAxisAlignment: StacMainAxisAlignment.center, + crossAxisAlignment: StacCrossAxisAlignment.center, + children: [ + StacRow( + mainAxisAlignment: StacMainAxisAlignment.center, + crossAxisAlignment: StacCrossAxisAlignment.center, + children: [ + StacSwitch(switchType: StacSwitchType.cupertino, value: true), + StacSizedBox(width: 20), + StacSwitch(switchType: StacSwitchType.adaptive, value: true), + StacSizedBox(width: 20), + StacSwitch(switchType: StacSwitchType.material, value: false), + ], + ), + StacSizedBox(height: 12), + ], + ), + ], + ), + ); +} diff --git a/stac_playground/lib/dsl/tab_bar.dart b/stac_playground/lib/dsl/tab_bar.dart new file mode 100644 index 00000000..347360e2 --- /dev/null +++ b/stac_playground/lib/dsl/tab_bar.dart @@ -0,0 +1,20 @@ +import 'package:stac_core/stac_core.dart'; + +@StacScreen(screenName: 'tab_bar') +StacWidget tabBarExample() { + return StacDefaultTabController( + length: 3, + child: StacScaffold( + appBar: StacAppBar( + title: StacText(data: 'Tabbar'), + bottom: StacTabBar(tabs: [ + StacTab(text: 'Red'), + StacTab(text: 'Red'), + StacTab(text: 'Red') + ])), + body: StacTabBarView(children: [ + StacContainer(color: '#D9D9D9'), + StacContainer(color: '#FC3F1B'), + StacContainer(color: '#D9D9D9') + ]))); +} diff --git a/stac_playground/lib/dsl/table.dart b/stac_playground/lib/dsl/table.dart new file mode 100644 index 00000000..24498518 --- /dev/null +++ b/stac_playground/lib/dsl/table.dart @@ -0,0 +1,67 @@ +import 'package:stac_core/stac_core.dart'; + +@StacScreen(screenName: 'table') +StacWidget tableExample() { + return StacScaffold( + appBar: StacAppBar(title: StacText(data: 'Stac Table Example')), + body: StacPadding( + padding: StacEdgeInsets.all(16.0), + child: StacTable( + defaultColumnWidth: StacTableColumnWidth( + type: StacTableColumnWidthType.flexColumnWidth, + value: 1, + ), + border: StacTableBorder(color: '#000000', width: 1.0), + children: [ + StacTableRow( + children: [ + _headerCell('Header 1'), + _headerCell('Header 2'), + _headerCell('Header 3'), + ], + ), + StacTableRow( + children: [ + _bodyCell('Row 1, Cell 1'), + _bodyCell('Row 1, Cell 2'), + _bodyCell('Row 1, Cell 3'), + ], + ), + StacTableRow( + children: [ + _bodyCell('Row 2, Cell 1'), + _bodyCell('Row 2, Cell 2'), + _bodyCell('Row 2, Cell 3'), + ], + ), + StacTableRow( + children: [ + _bodyCell('Row 3, Cell 1'), + _bodyCell('Row 3, Cell 2'), + _bodyCell('Row 3, Cell 3'), + ], + ), + ], + ), + ), + ); +} + +StacWidget _headerCell(String data) { + return StacTableCell( + child: StacContainer( + color: '#40000000', + height: 50.0, + child: StacCenter(child: StacText(data: data)), + ), + ); +} + +StacWidget _bodyCell(String data) { + return StacTableCell( + child: StacSizedBox( + height: 50.0, + child: StacCenter(child: StacText(data: data)), + ), + ); +} diff --git a/stac_playground/lib/dsl/text.dart b/stac_playground/lib/dsl/text.dart new file mode 100644 index 00000000..5efc2e66 --- /dev/null +++ b/stac_playground/lib/dsl/text.dart @@ -0,0 +1,36 @@ +import 'package:stac_core/stac_core.dart'; + +@StacScreen(screenName: 'text') +StacWidget textExample() { + return StacScaffold( + appBar: StacAppBar(title: StacText(data: 'Text')), + body: StacPadding( + padding: StacEdgeInsets.only(top: 12, left: 12, right: 12), + child: StacColumn( + mainAxisAlignment: StacMainAxisAlignment.start, + crossAxisAlignment: StacCrossAxisAlignment.start, + children: [ + StacSizedBox(height: 12), + StacText( + data: 'Flutter', + style: StacTextStyle( + fontSize: 23, fontWeight: StacFontWeight.w600)), + StacSizedBox(height: 32), + StacText(data: 'This is a normal Text.'), + StacSizedBox(height: 16), + StacText(data: 'This is a ', children: [ + StacTextSpan( + text: 'Rich Text.', + style: StacTextStyle( + fontWeight: StacFontWeight.w800, color: '#6750A4'), + onTap: { + 'actionType': 'navigate', + 'navigationStyle': 'push', + 'widgetJson': { + 'type': 'exampleScreen', + 'assetPath': 'assets/json/web_view_example.json', + }, + }) + ]) + ]))); +} diff --git a/stac_playground/lib/dsl/text_button.dart b/stac_playground/lib/dsl/text_button.dart new file mode 100644 index 00000000..6c5b5a2b --- /dev/null +++ b/stac_playground/lib/dsl/text_button.dart @@ -0,0 +1,66 @@ +import 'package:stac_core/stac_core.dart'; + +@StacScreen(screenName: 'text_button') +StacWidget textButtonExample() { + return StacScaffold( + appBar: StacAppBar(title: StacText(data: 'Text Button')), + body: StacRow( + mainAxisAlignment: StacMainAxisAlignment.center, + crossAxisAlignment: StacCrossAxisAlignment.center, + children: [ + StacColumn( + mainAxisAlignment: StacMainAxisAlignment.center, + crossAxisAlignment: StacCrossAxisAlignment.center, + children: [ + StacRow( + mainAxisAlignment: StacMainAxisAlignment.center, + crossAxisAlignment: StacCrossAxisAlignment.center, + children: [ + StacTextButton( + child: StacText(data: 'BUTTON'), + style: StacButtonStyle( + padding: StacEdgeInsets.only( + top: 8, left: 12, right: 12, bottom: 8)), + onPressed: StacAction()), + StacSizedBox(width: 20), + StacTextButton( + child: StacRow(children: [ + StacIcon( + iconType: StacIconType.material, + icon: 'add', + size: 18), + StacSizedBox(width: 4), + StacText(data: 'BUTTON') + ]), + style: StacButtonStyle( + padding: StacEdgeInsets.only( + top: 8, left: 12, right: 12, bottom: 8)), + onPressed: StacAction()) + ]), + StacSizedBox(height: 12), + StacRow( + mainAxisAlignment: StacMainAxisAlignment.center, + crossAxisAlignment: StacCrossAxisAlignment.center, + children: [ + StacTextButton( + child: StacText(data: 'BUTTON'), + style: StacButtonStyle( + padding: StacEdgeInsets.only( + top: 8, left: 12, right: 12, bottom: 8))), + StacSizedBox(width: 20), + StacTextButton( + child: StacRow(children: [ + StacIcon( + iconType: StacIconType.material, + icon: 'add', + size: 18), + StacSizedBox(width: 4), + StacText(data: 'BUTTON') + ]), + style: StacButtonStyle( + padding: StacEdgeInsets.only( + top: 8, left: 12, right: 12, bottom: 8))) + ]) + ]) + ])); +} diff --git a/stac_playground/lib/dsl/text_field.dart b/stac_playground/lib/dsl/text_field.dart new file mode 100644 index 00000000..acc7ff9e --- /dev/null +++ b/stac_playground/lib/dsl/text_field.dart @@ -0,0 +1,183 @@ +import 'package:stac_core/stac_core.dart'; + +@StacScreen(screenName: 'text_field') +StacWidget textFieldExample() { + return StacScaffold( + appBar: StacAppBar(title: StacText(data: 'Text Field')), + body: StacSingleChildScrollView( + child: StacContainer( + padding: StacEdgeInsets.all(12), + child: StacColumn( + mainAxisAlignment: StacMainAxisAlignment.center, + crossAxisAlignment: StacCrossAxisAlignment.center, + children: [ + StacSizedBox(height: 24), + StacTextField( + maxLines: 1, + keyboardType: StacTextInputType.text, + textInputAction: StacTextInputAction.done, + textAlign: StacTextAlign.start, + textCapitalization: StacTextCapitalization.none, + textDirection: StacTextDirection.ltr, + obscureText: false, + decoration: StacInputDecoration( + hintText: 'What do people call you?', + filled: true, + icon: StacIcon( + iconType: StacIconType.cupertino, + icon: 'person_solid', + size: 24, + ), + labelText: 'Name*', + ), + readOnly: false, + enabled: true, + ), + StacSizedBox(height: 24), + StacTextField( + maxLines: 1, + keyboardType: StacTextInputType.text, + textInputAction: StacTextInputAction.done, + textAlign: StacTextAlign.start, + textCapitalization: StacTextCapitalization.none, + textDirection: StacTextDirection.ltr, + obscureText: false, + decoration: StacInputDecoration( + hintText: 'Where can we reach you?', + filled: true, + icon: StacIcon( + iconType: StacIconType.cupertino, + icon: 'phone_solid', + size: 24, + ), + labelText: 'Phone number*', + ), + readOnly: false, + enabled: true, + ), + StacSizedBox(height: 24), + StacTextField( + maxLines: 1, + keyboardType: StacTextInputType.text, + textInputAction: StacTextInputAction.done, + textAlign: StacTextAlign.start, + textCapitalization: StacTextCapitalization.none, + textDirection: StacTextDirection.ltr, + obscureText: false, + decoration: StacInputDecoration( + hintText: 'Your email address', + filled: true, + icon: StacIcon( + iconType: StacIconType.material, + icon: 'email', + size: 24, + ), + labelText: 'Email', + ), + readOnly: false, + enabled: true, + ), + StacSizedBox(height: 24), + StacTextField( + maxLines: 1, + keyboardType: StacTextInputType.text, + textInputAction: StacTextInputAction.done, + textAlign: StacTextAlign.start, + textCapitalization: StacTextCapitalization.none, + textDirection: StacTextDirection.ltr, + obscureText: false, + decoration: StacInputDecoration( + filled: true, + icon: StacIcon( + iconType: StacIconType.material, + icon: 'credit_card', + size: 24, + ), + hintText: 'XXXX XXXX XXXX XXXX', + labelText: 'Credit Card', + ), + readOnly: false, + enabled: true, + ), + StacSizedBox(height: 24), + StacSizedBox( + height: 100, + child: StacTextField( + expands: true, + decoration: StacInputDecoration( + filled: true, + labelText: 'Life story', + ), + readOnly: false, + enabled: true, + ), + ), + StacSizedBox(height: 24), + StacTextField( + maxLines: 1, + keyboardType: StacTextInputType.text, + textInputAction: StacTextInputAction.done, + textAlign: StacTextAlign.start, + textCapitalization: StacTextCapitalization.none, + textDirection: StacTextDirection.ltr, + obscureText: true, + decoration: StacInputDecoration( + filled: true, + suffixIcon: StacIcon( + iconType: StacIconType.cupertino, + icon: 'eye', + size: 24, + ), + labelText: 'Password*', + ), + readOnly: false, + enabled: true, + ), + StacSizedBox(height: 24), + StacTextField( + maxLines: 1, + keyboardType: StacTextInputType.text, + textInputAction: StacTextInputAction.done, + textAlign: StacTextAlign.start, + textCapitalization: StacTextCapitalization.none, + textDirection: StacTextDirection.ltr, + obscureText: true, + decoration: StacInputDecoration( + filled: true, + suffixIcon: StacIcon( + iconType: StacIconType.cupertino, + icon: 'eye', + size: 24, + ), + labelText: 'Re-type password*', + ), + readOnly: false, + enabled: true, + ), + StacSizedBox(height: 24), + StacTextField( + maxLines: 1, + keyboardType: StacTextInputType.text, + textInputAction: StacTextInputAction.done, + textAlign: StacTextAlign.start, + textCapitalization: StacTextCapitalization.none, + textDirection: StacTextDirection.ltr, + obscureText: true, + decoration: StacInputDecoration( + filled: true, + labelText: 'Gradient TextField', + border: StacInputBorder( + type: StacInputBorderType.outlineInputBorder, + borderRadius: StacBorderRadius.all(80), + gradient: StacGradient(colors: ['#FF0000', '#0000FF']), + ), + ), + readOnly: false, + enabled: true, + ), + ], + ), + ), + ), + ); +} diff --git a/stac_playground/lib/dsl/tool_tip.dart b/stac_playground/lib/dsl/tool_tip.dart new file mode 100644 index 00000000..9f67ef62 --- /dev/null +++ b/stac_playground/lib/dsl/tool_tip.dart @@ -0,0 +1,107 @@ +import 'package:stac_core/stac_core.dart'; + +@StacScreen(screenName: 'tool_tip') +StacWidget toolTipExample() { + return StacScaffold( + appBar: StacAppBar(title: StacText(data: 'Tooltip')), + body: StacColumn( + mainAxisAlignment: StacMainAxisAlignment.center, + crossAxisAlignment: StacCrossAxisAlignment.center, + children: [ + StacText( + data: 'Basic Tooltip', + style: StacTextStyle(fontSize: 18, fontWeight: StacFontWeight.bold), + ), + StacSizedBox(height: 16), + StacRow( + mainAxisAlignment: StacMainAxisAlignment.center, + children: [ + StacTooltip( + message: 'This is a basic tooltip', + child: StacIcon(icon: 'info', size: 32), + ), + ], + ), + StacSizedBox(height: 32), + StacText( + data: 'Styled Tooltip', + style: StacTextStyle(fontSize: 18, fontWeight: StacFontWeight.bold), + ), + StacSizedBox(height: 16), + StacRow( + mainAxisAlignment: StacMainAxisAlignment.center, + children: [ + StacTooltip( + message: 'Custom styled tooltip', + preferBelow: false, + verticalOffset: 12, + decoration: StacBoxDecoration( + color: '#4CAF50', + borderRadius: StacBorderRadius.only( + topLeft: 6, + topRight: 6, + bottomLeft: 6, + bottomRight: 6, + ), + ), + textStyle: StacTextStyle( + color: '#FFFFFF', + fontSize: 14, + fontWeight: StacFontWeight.bold, + ), + child: StacIcon(icon: 'palette', size: 32), + ), + ], + ), + StacSizedBox(height: 32), + StacText( + data: 'Tooltip with Delay & Duration', + style: StacTextStyle(fontSize: 18, fontWeight: StacFontWeight.bold), + ), + StacSizedBox(height: 16), + StacRow( + mainAxisAlignment: StacMainAxisAlignment.center, + children: [ + StacTooltip( + message: 'Appears after 1s, stays 3s', + waitDuration: StacDuration(milliseconds: 1000), + showDuration: StacDuration(milliseconds: 3000), + exitDuration: StacDuration(milliseconds: 300), + child: StacIcon(icon: 'timer', size: 32), + ), + ], + ), + StacSizedBox(height: 32), + StacText( + data: 'Tooltip on IconButton', + style: StacTextStyle(fontSize: 18, fontWeight: StacFontWeight.bold), + ), + StacSizedBox(height: 16), + StacRow( + mainAxisAlignment: StacMainAxisAlignment.center, + children: [ + StacTooltip( + message: 'Notifications', + child: StacIconButton( + icon: StacIcon(icon: 'notifications', size: 24), + padding: + StacEdgeInsets.only(left: 0, top: 0, right: 0, bottom: 0), + onPressed: StacAction(jsonData: {'actionType': 'none'}), + ), + ), + ], + ), + StacText( + data: 'Tap to see Tooltip', + style: StacTextStyle(fontSize: 18, fontWeight: StacFontWeight.bold), + ), + StacSizedBox(height: 16), + StacTooltip( + message: 'Tap to see tooltip', + triggerMode: StacTooltipTriggerMode.tap, + child: StacIcon(icon: 'info', size: 32), + ), + ], + ), + ); +} diff --git a/stac_playground/lib/dsl/variable.dart b/stac_playground/lib/dsl/variable.dart new file mode 100644 index 00000000..03686515 --- /dev/null +++ b/stac_playground/lib/dsl/variable.dart @@ -0,0 +1,39 @@ +import 'package:stac_core/stac_core.dart'; + +@StacScreen(screenName: 'variable') +StacWidget variableExample() { + return StacSetValue( + values: >[ + {'key': 'name', 'value': 'John Doe'}, + {'key': 'age', 'value': 30}, + {'key': 'city', 'value': 'New York'}, + {'key': 'country', 'value': 'USA'}, + ], + child: StacScaffold( + body: StacCenter( + child: StacColumn( + mainAxisAlignment: StacMainAxisAlignment.center, + children: [ + StacText(data: '{{name}}'), + StacText(data: '{{age}}'), + StacText( + data: '{{city}}', + children: [StacTextSpan(text: '{{country}}')], + ), + ], + ), + ), + floatingActionButton: StacFloatingActionButton( + child: StacIcon(icon: 'add'), + onPressed: StacSetValueAction( + values: >[ + {'key': 'phone', 'value': '1234567890'}, + ], + action: StacNavigateAction( + assetPath: 'assets/json/variable_navigate_example.json', + ), + ), + ), + ), + ); +} diff --git a/stac_playground/lib/dsl/variable_navigate.dart b/stac_playground/lib/dsl/variable_navigate.dart new file mode 100644 index 00000000..97f9d6d0 --- /dev/null +++ b/stac_playground/lib/dsl/variable_navigate.dart @@ -0,0 +1,16 @@ +import 'package:stac_core/stac_core.dart'; + +@StacScreen(screenName: 'variable_navigate') +StacWidget variableNavigateExample() { + return StacScaffold( + body: StacCenter( + child: StacColumn( + mainAxisAlignment: StacMainAxisAlignment.center, + children: [ + StacText(data: '{{name}}'), + StacText(data: '{{age}}'), + StacText( + data: '{{city}} ', children: [StacTextSpan(text: '{{country}}')]), + StacText(data: 'phone: {{phone}}') + ]))); +} diff --git a/stac_playground/lib/dsl/web_view.dart b/stac_playground/lib/dsl/web_view.dart new file mode 100644 index 00000000..b586f142 --- /dev/null +++ b/stac_playground/lib/dsl/web_view.dart @@ -0,0 +1,10 @@ +import 'package:stac_core/stac_core.dart'; +import 'package:stac_webview/stac_webview.dart'; + +@StacScreen(screenName: 'web_view') +StacWidget webViewExample() { + return StacScaffold( + appBar: StacAppBar(title: StacText(data: 'WebView')), + body: StacWebView(url: 'https://github.com/StacDev/stac'), + ); +} diff --git a/stac_playground/lib/dsl/wrap.dart b/stac_playground/lib/dsl/wrap.dart new file mode 100644 index 00000000..98240ff2 --- /dev/null +++ b/stac_playground/lib/dsl/wrap.dart @@ -0,0 +1,80 @@ +import 'package:stac_core/stac_core.dart'; + +@StacScreen(screenName: 'wrap') +StacWidget wrapExample() { + return StacScaffold( + appBar: StacAppBar(title: StacText(data: 'Wrap Demo')), + body: StacCenter( + child: StacWrap(spacing: 8, runSpacing: 4, children: [ + StacContainer( + color: '#FFCDD2', + width: 100, + height: 100, + child: StacCenter( + child: StacText( + data: '1', style: StacTextStyle(color: '#FFFFFF')))), + StacContainer( + color: '#F8BBD0', + width: 100, + height: 100, + child: StacCenter( + child: StacText( + data: '2', style: StacTextStyle(color: '#FFFFFF')))), + StacContainer( + color: '#E1BEE7', + width: 100, + height: 100, + child: StacCenter( + child: StacText( + data: '3', style: StacTextStyle(color: '#FFFFFF')))), + StacContainer( + color: '#D1C4E9', + width: 100, + height: 100, + child: StacCenter( + child: StacText( + data: '4', style: StacTextStyle(color: '#FFFFFF')))), + StacContainer( + color: '#C5CAE9', + width: 100, + height: 100, + child: StacCenter( + child: StacText( + data: '5', style: StacTextStyle(color: '#FFFFFF')))), + StacContainer( + color: '#BBDEFB', + width: 100, + height: 100, + child: StacCenter( + child: StacText( + data: '6', style: StacTextStyle(color: '#FFFFFF')))), + StacContainer( + color: '#B3E5FC', + width: 100, + height: 100, + child: StacCenter( + child: StacText( + data: '7', style: StacTextStyle(color: '#FFFFFF')))), + StacContainer( + color: '#B2EBF2', + width: 100, + height: 100, + child: StacCenter( + child: StacText( + data: '8', style: StacTextStyle(color: '#FFFFFF')))), + StacContainer( + color: '#B2DFDB', + width: 100, + height: 100, + child: StacCenter( + child: StacText( + data: '9', style: StacTextStyle(color: '#FFFFFF')))), + StacContainer( + color: '#C8E6C9', + width: 100, + height: 100, + child: StacCenter( + child: StacText( + data: '10', style: StacTextStyle(color: '#FFFFFF')))) + ]))); +} diff --git a/stac_playground/lib/main.dart b/stac_playground/lib/main.dart new file mode 100644 index 00000000..24a355c0 --- /dev/null +++ b/stac_playground/lib/main.dart @@ -0,0 +1,47 @@ +import 'package:flutter/foundation.dart' show kIsWeb; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:stac/stac.dart'; +import 'package:stac_playground/app/cubit/home_cubit.dart'; +import 'package:stac_playground/app/embed/embed_screen_stub.dart' + if (dart.library.js_interop) 'package:stac_playground/app/embed/embed_screen.dart'; +import 'package:stac_playground/app/home_screen.dart'; +import 'package:stac_playground/theme/app_theme.dart'; +import 'package:stac_webview/stac_webview.dart'; + +void main() async { + WidgetsFlutterBinding.ensureInitialized(); + // webView lives in the stac_webview plugin rather than core, so it needs + // registering explicitly. webview_flutter has no web implementation, and the + // widget builds its controller in initState — outside the framework's + // try/catch — so registering it on web would render a Flutter error box. + // Leaving it unregistered there lets Stac log an unsupported-type warning + // and degrade quietly instead. + await Stac.initialize( + parsers: [if (!kIsWeb) const StacWebViewParser()], + ); + + runApp(const MyApp()); +} + +class MyApp extends StatelessWidget { + const MyApp({super.key}); + + @override + Widget build(BuildContext context) { + final initialRoute = Uri.base.path; + + return MaterialApp( + title: 'Stac Playground', + theme: AppTheme.darkTheme, + initialRoute: initialRoute, + routes: { + '/': (_) => BlocProvider( + create: (_) => HomeCubit(), + child: const HomeScreen(), + ), + '/embed': (_) => const EmbedScreen(), + }, + ); + } +} diff --git a/stac_playground/lib/theme/app_colors.dart b/stac_playground/lib/theme/app_colors.dart new file mode 100644 index 00000000..0a71d93b --- /dev/null +++ b/stac_playground/lib/theme/app_colors.dart @@ -0,0 +1,162 @@ +import 'package:flutter/material.dart'; + +@immutable +class AppColors extends ThemeExtension { + const AppColors({ + required this.brightness, + required this.primary, + required this.onPrimary, + required this.secondary, + required this.onSecondary, + required this.background, + required this.onBackground, + required this.onBackground2, + required this.onBackground3, + required this.surface, + required this.onSurface, + required this.surfaceVariant, + required this.onSurfaceVariant, + required this.error, + required this.onError, + required this.success, + required this.onSuccess, + required this.warning, + required this.onWarning, + required this.outline, + required this.outline2, + required this.primaryGradient, + required this.backgroundGradient, + required this.cardGradient, + required this.lineGradient, + }); + + final Brightness brightness; + final Color primary; + final Color onPrimary; + final Color secondary; + final Color onSecondary; + final Color background; + final Color onBackground; + final Color onBackground2; + final Color onBackground3; + final Color surface; + final Color onSurface; + final Color surfaceVariant; + final Color onSurfaceVariant; + final Color error; + final Color onError; + final Color success; + final Color onSuccess; + final Color warning; + final Color onWarning; + final Color outline; + final Color outline2; + final LinearGradient primaryGradient; + final LinearGradient backgroundGradient; + final LinearGradient cardGradient; + final LinearGradient lineGradient; + + @override + ThemeExtension copyWith({ + Brightness? brightness, + Color? primary, + Color? onPrimary, + Color? secondary, + Color? onSecondary, + Color? background, + Color? onBackground, + Color? onBackground2, + Color? onBackground3, + Color? surface, + Color? onSurface, + Color? surfaceVariant, + Color? onSurfaceVariant, + Color? error, + Color? onError, + Color? success, + Color? onSuccess, + Color? warning, + Color? onWarning, + Color? outline, + Color? outline2, + LinearGradient? primaryGradient, + LinearGradient? backgroundGradient, + LinearGradient? cardGradient, + LinearGradient? lineGradient, + }) { + return AppColors( + brightness: brightness ?? this.brightness, + primary: primary ?? this.primary, + onPrimary: onPrimary ?? this.onPrimary, + secondary: secondary ?? this.secondary, + onSecondary: onSecondary ?? this.onSecondary, + background: background ?? this.background, + onBackground: onBackground ?? this.onBackground, + onBackground2: onBackground2 ?? this.onBackground2, + onBackground3: onBackground3 ?? this.onBackground3, + surface: surface ?? this.surface, + onSurface: onSurface ?? this.onSurface, + surfaceVariant: surfaceVariant ?? this.surfaceVariant, + onSurfaceVariant: onSurfaceVariant ?? this.onSurfaceVariant, + error: error ?? this.error, + onError: onError ?? this.onError, + success: success ?? this.success, + onSuccess: onSuccess ?? this.onSuccess, + warning: warning ?? this.warning, + onWarning: onWarning ?? this.onWarning, + outline: outline ?? this.outline, + outline2: outline2 ?? this.outline2, + primaryGradient: primaryGradient ?? this.primaryGradient, + backgroundGradient: backgroundGradient ?? this.backgroundGradient, + cardGradient: cardGradient ?? this.cardGradient, + lineGradient: lineGradient ?? this.lineGradient, + ); + } + + @override + ThemeExtension lerp(ThemeExtension? other, double t) { + if (other is! AppColors) { + return this; + } + + return AppColors( + brightness: brightness, + primary: Color.lerp(primary, other.primary, t) ?? primary, + onPrimary: Color.lerp(onPrimary, other.onPrimary, t) ?? onPrimary, + secondary: Color.lerp(secondary, other.secondary, t) ?? secondary, + onSecondary: Color.lerp(onSecondary, other.onSecondary, t) ?? onSecondary, + background: Color.lerp(background, other.background, t) ?? background, + onBackground: + Color.lerp(onBackground, other.onBackground, t) ?? onBackground, + onBackground2: + Color.lerp(onBackground2, other.onBackground2, t) ?? onBackground2, + onBackground3: + Color.lerp(onBackground3, other.onBackground3, t) ?? onBackground3, + surface: Color.lerp(surface, other.surface, t) ?? surface, + onSurface: Color.lerp(onSurface, other.onSurface, t) ?? onSurface, + surfaceVariant: + Color.lerp(surfaceVariant, other.surfaceVariant, t) ?? surfaceVariant, + onSurfaceVariant: + Color.lerp(onSurfaceVariant, other.onSurfaceVariant, t) ?? + onSurfaceVariant, + error: Color.lerp(error, other.error, t) ?? error, + onError: Color.lerp(onError, other.onError, t) ?? onError, + success: Color.lerp(success, other.success, t) ?? success, + onSuccess: Color.lerp(onSuccess, other.onSuccess, t) ?? onSuccess, + warning: Color.lerp(warning, other.warning, t) ?? warning, + onWarning: Color.lerp(onWarning, other.onWarning, t) ?? onWarning, + outline: Color.lerp(outline, other.outline, t) ?? outline, + outline2: Color.lerp(outline2, other.outline2, t) ?? outline2, + primaryGradient: + LinearGradient.lerp(primaryGradient, other.primaryGradient, t) ?? + primaryGradient, + backgroundGradient: LinearGradient.lerp( + backgroundGradient, other.backgroundGradient, t) ?? + backgroundGradient, + cardGradient: LinearGradient.lerp(cardGradient, other.cardGradient, t) ?? + cardGradient, + lineGradient: LinearGradient.lerp(lineGradient, other.lineGradient, t) ?? + lineGradient, + ); + } +} diff --git a/stac_playground/lib/theme/app_text_theme.dart b/stac_playground/lib/theme/app_text_theme.dart new file mode 100644 index 00000000..d66b0d5d --- /dev/null +++ b/stac_playground/lib/theme/app_text_theme.dart @@ -0,0 +1,109 @@ +import 'package:flutter/material.dart'; + +class AppTextTheme { + const AppTextTheme._(); + + static const TextTheme textTheme = TextTheme( + displayLarge: displayLarge, + displayMedium: displayMedium, + displaySmall: displaySmall, + headlineLarge: headlineLarge, + headlineMedium: headlineMedium, + headlineSmall: headlineSmall, + titleLarge: titleLarge, + titleMedium: titleMedium, + titleSmall: titleSmall, + bodyLarge: bodyLarge, + bodyMedium: bodyMedium, + bodySmall: bodySmall, + labelLarge: labelLarge, + labelMedium: labelMedium, + labelSmall: labelSmall, + ); + + static const TextStyle displayLarge = TextStyle( + fontSize: 96, + fontWeight: FontWeight.w800, + height: 1, + letterSpacing: -1.92, + ); + static const TextStyle displayMedium = TextStyle( + fontSize: 66, + fontWeight: FontWeight.w800, + ); + static const TextStyle displaySmall = TextStyle( + fontSize: 40, + fontWeight: FontWeight.w800, + height: 1.3, + letterSpacing: -0.4, + ); + + static const TextStyle headlineLarge = TextStyle( + fontSize: 32, + fontWeight: FontWeight.w700, + height: 1.3, + letterSpacing: -0.32, + ); + static const TextStyle headlineMedium = TextStyle( + fontSize: 28, + fontWeight: FontWeight.w700, + height: 1.4, + ); + static const TextStyle headlineSmall = TextStyle( + fontSize: 24, + fontWeight: FontWeight.w700, + height: 1.5, + ); + + static const TextStyle titleLarge = TextStyle( + fontSize: 20, + fontWeight: FontWeight.w700, + height: 1.5, + ); + + static const TextStyle titleMedium = TextStyle( + fontSize: 18, + fontWeight: FontWeight.w500, + ); + static const TextStyle titleSmall = TextStyle( + fontSize: 14, + fontWeight: FontWeight.w500, + ); + + static const TextStyle labelLarge = TextStyle( + fontSize: 13, + fontWeight: FontWeight.w600, + height: 1.5, + letterSpacing: 0.065, + ); + + static const TextStyle labelMedium = TextStyle( + fontSize: 12, + fontWeight: FontWeight.w400, + height: 1.5, + letterSpacing: 0.06, + ); + static const TextStyle labelSmall = TextStyle( + fontSize: 11, + fontWeight: FontWeight.w500, + ); + + static const TextStyle bodyLarge = TextStyle( + fontSize: 18, + fontWeight: FontWeight.w400, + height: 1.5, + letterSpacing: 0.09, + ); + static const TextStyle bodyMedium = TextStyle( + fontSize: 15, + fontWeight: FontWeight.w400, + height: 1.5, + letterSpacing: 0.075, + ); + static const TextStyle bodySmall = TextStyle( + fontSize: 15, + fontWeight: FontWeight.w400, + height: 1.5, + letterSpacing: 0.07, + ); +} diff --git a/stac_playground/lib/theme/app_theme.dart b/stac_playground/lib/theme/app_theme.dart new file mode 100644 index 00000000..f772af8a --- /dev/null +++ b/stac_playground/lib/theme/app_theme.dart @@ -0,0 +1,120 @@ +import 'package:flutter/material.dart'; +import 'package:stac_playground/theme/app_colors.dart'; +import 'package:stac_playground/theme/app_text_theme.dart'; + +class AppTheme { + const AppTheme._(); + + /// Console palette — mirrors the Stac Console design tokens: + /// surface #0B0B0D, surface-bright #101112, white-alpha outlines, + /// secondary (accent) #50D59D, warning #FF9A3E. + static AppColors get appColors => const AppColors( + brightness: Brightness.dark, + primary: Color(0xFF50D59D), + onPrimary: Color(0XFF0B0B0D), + secondary: Color(0XFF50D59D), + onSecondary: Color(0XFF0B0B0D), + background: Color(0XFF0B0B0D), + onBackground: Color(0XFFFFFFFF), + onBackground2: Color(0XB3FFFFFF), + onBackground3: Color(0X4DFFFFFF), + surface: Color(0XFF101112), + onSurface: Color(0XFFFFFFFF), + surfaceVariant: Color(0X0AFFFFFF), + onSurfaceVariant: Color(0X99FFFFFF), + error: Color(0XFFFF6565), + onError: Color(0XFF0B0B0D), + success: Color(0XFF50D59D), + onSuccess: Color(0XFF0B0B0D), + warning: Color(0XFFFF9A3E), + onWarning: Color(0XFF0B0B0D), + outline: Color(0X0FFFFFFF), + outline2: Color(0X1AFFFFFF), + primaryGradient: LinearGradient( + colors: [ + Color(0xFF27BA68), + Color(0xFF50D59D), + ], + ), + backgroundGradient: LinearGradient( + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + colors: [Color(0xFF0B0B0D), Color(0xFF101112)], + stops: [0.5, 1.0], + ), + cardGradient: LinearGradient( + begin: Alignment(0.00, -1.00), + end: Alignment(0, 1), + colors: [ + Color(0x06FFFFFF), + Color(0x03FFFFFF), + ], + ), + lineGradient: LinearGradient( + begin: Alignment(0.00, -1.00), + end: Alignment(0, 1), + colors: [ + Color(0x06FFFFFF), + Color(0x03FFFFFF), + ], + ), + ); + + static get darkTheme { + final baseTheme = ThemeData( + useMaterial3: true, + scaffoldBackgroundColor: appColors.background, + colorScheme: ColorScheme( + brightness: appColors.brightness, + primary: appColors.primary, + onPrimary: appColors.onPrimary, + secondary: appColors.secondary, + onSecondary: appColors.onSecondary, + surface: appColors.surface, + onSurface: appColors.onSurface, + surfaceContainerHighest: appColors.surfaceVariant, + onSurfaceVariant: appColors.onSurfaceVariant, + error: appColors.error, + onError: appColors.onError, + ), + fontFamily: 'Figtree', + textTheme: AppTextTheme.textTheme, + + /// Component Theme + appBarTheme: const AppBarTheme( + backgroundColor: Colors.transparent, + elevation: 0, + ), + tooltipTheme: const TooltipThemeData( + constraints: BoxConstraints(minHeight: 24), + textStyle: TextStyle( + fontSize: 12, + height: 1.3, + color: Colors.white, + ), + decoration: BoxDecoration( + color: Color(0xFF272A2C), + ), + ), + iconButtonTheme: IconButtonThemeData( + style: IconButton.styleFrom( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8), + ), + ), + ), + + ///Extensions + extensions: [ + appColors, + ], + ); + + return baseTheme; + } +} + +extension AppThemeX on BuildContext { + AppColors get colors => Theme.of(this).extension()!; + TextTheme get textTheme => Theme.of(this).textTheme; +} diff --git a/stac_playground/lib/utils/log.dart b/stac_playground/lib/utils/log.dart new file mode 100644 index 00000000..e1885d13 --- /dev/null +++ b/stac_playground/lib/utils/log.dart @@ -0,0 +1,26 @@ +import 'package:logger/logger.dart'; + +class Log { + const Log._(); + + static final _log = Logger(); + + static void d(dynamic message) => _log.d(message); + static void i(dynamic message) => _log.i(message); + static void w(dynamic message) => _log.w(message); + static void e(dynamic message) => _log.e(message); + + static void error( + {String? tag, + Object? object, + String? message, + Object? err, + StackTrace? trace}) { + var tagStr = tag ?? ''; + if (object != null) { + tagStr += '(${object.runtimeType.toString()})'; + } + + _log.e('$tagStr: ${message ?? ''}', err, trace); + } +} diff --git a/examples/stac_gallery/linux/.gitignore b/stac_playground/linux/.gitignore similarity index 100% rename from examples/stac_gallery/linux/.gitignore rename to stac_playground/linux/.gitignore diff --git a/examples/stac_gallery/linux/CMakeLists.txt b/stac_playground/linux/CMakeLists.txt similarity index 83% rename from examples/stac_gallery/linux/CMakeLists.txt rename to stac_playground/linux/CMakeLists.txt index 23f9f32d..e13ee5a2 100644 --- a/examples/stac_gallery/linux/CMakeLists.txt +++ b/stac_playground/linux/CMakeLists.txt @@ -1,13 +1,13 @@ # Project-level configuration. -cmake_minimum_required(VERSION 3.10) +cmake_minimum_required(VERSION 3.13) project(runner LANGUAGES CXX) # The name of the executable created for the application. Change this to change # the on-disk name of your application. -set(BINARY_NAME "stac_gallery") +set(BINARY_NAME "stac_playground") # The unique GTK application identifier for this application. See: # https://wiki.gnome.org/HowDoI/ChooseApplicationID -set(APPLICATION_ID "com.stac.stac_gallery") +set(APPLICATION_ID "com.stac.stac_playground") # Explicitly opt in to modern CMake behaviors to avoid warnings with recent # versions of CMake. @@ -54,25 +54,8 @@ add_subdirectory(${FLUTTER_MANAGED_DIR}) find_package(PkgConfig REQUIRED) pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0) -add_definitions(-DAPPLICATION_ID="${APPLICATION_ID}") - -# Define the application target. To change its name, change BINARY_NAME above, -# not the value here, or `flutter run` will no longer work. -# -# Any new source files that you add to the application should be added here. -add_executable(${BINARY_NAME} - "main.cc" - "my_application.cc" - "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" -) - -# Apply the standard set of build settings. This can be removed for applications -# that need different build settings. -apply_standard_settings(${BINARY_NAME}) - -# Add dependency libraries. Add any application-specific dependencies here. -target_link_libraries(${BINARY_NAME} PRIVATE flutter) -target_link_libraries(${BINARY_NAME} PRIVATE PkgConfig::GTK) +# Application build; see runner/CMakeLists.txt. +add_subdirectory("runner") # Run the Flutter tool portions of the build. This must not be removed. add_dependencies(${BINARY_NAME} flutter_assemble) @@ -86,6 +69,7 @@ set_target_properties(${BINARY_NAME} RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/intermediates_do_not_run" ) + # Generated plugin build rules, which manage building the plugins and adding # them to the application. include(flutter/generated_plugins.cmake) @@ -122,6 +106,12 @@ foreach(bundled_library ${PLUGIN_BUNDLED_LIBRARIES}) COMPONENT Runtime) endforeach(bundled_library) +# Copy the native assets provided by the build.dart from all packages. +set(NATIVE_ASSETS_DIR "${PROJECT_BUILD_DIR}native_assets/linux/") +install(DIRECTORY "${NATIVE_ASSETS_DIR}" + DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) + # Fully re-copy the assets directory on each build to avoid having stale files # from a previous install. set(FLUTTER_ASSET_DIR_NAME "flutter_assets") diff --git a/examples/stac_gallery/linux/flutter/CMakeLists.txt b/stac_playground/linux/flutter/CMakeLists.txt similarity index 100% rename from examples/stac_gallery/linux/flutter/CMakeLists.txt rename to stac_playground/linux/flutter/CMakeLists.txt diff --git a/stac_playground/linux/flutter/generated_plugin_registrant.cc b/stac_playground/linux/flutter/generated_plugin_registrant.cc new file mode 100644 index 00000000..f6f23bfe --- /dev/null +++ b/stac_playground/linux/flutter/generated_plugin_registrant.cc @@ -0,0 +1,15 @@ +// +// Generated file. Do not edit. +// + +// clang-format off + +#include "generated_plugin_registrant.h" + +#include + +void fl_register_plugins(FlPluginRegistry* registry) { + g_autoptr(FlPluginRegistrar) url_launcher_linux_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "UrlLauncherPlugin"); + url_launcher_plugin_register_with_registrar(url_launcher_linux_registrar); +} diff --git a/examples/stac_gallery/linux/flutter/generated_plugin_registrant.h b/stac_playground/linux/flutter/generated_plugin_registrant.h similarity index 100% rename from examples/stac_gallery/linux/flutter/generated_plugin_registrant.h rename to stac_playground/linux/flutter/generated_plugin_registrant.h diff --git a/examples/stac_gallery/linux/flutter/generated_plugins.cmake b/stac_playground/linux/flutter/generated_plugins.cmake similarity index 97% rename from examples/stac_gallery/linux/flutter/generated_plugins.cmake rename to stac_playground/linux/flutter/generated_plugins.cmake index 2e1de87a..f16b4c34 100644 --- a/examples/stac_gallery/linux/flutter/generated_plugins.cmake +++ b/stac_playground/linux/flutter/generated_plugins.cmake @@ -3,6 +3,7 @@ # list(APPEND FLUTTER_PLUGIN_LIST + url_launcher_linux ) list(APPEND FLUTTER_FFI_PLUGIN_LIST diff --git a/stac_playground/linux/runner/CMakeLists.txt b/stac_playground/linux/runner/CMakeLists.txt new file mode 100644 index 00000000..e97dabc7 --- /dev/null +++ b/stac_playground/linux/runner/CMakeLists.txt @@ -0,0 +1,26 @@ +cmake_minimum_required(VERSION 3.13) +project(runner LANGUAGES CXX) + +# Define the application target. To change its name, change BINARY_NAME in the +# top-level CMakeLists.txt, not the value here, or `flutter run` will no longer +# work. +# +# Any new source files that you add to the application should be added here. +add_executable(${BINARY_NAME} + "main.cc" + "my_application.cc" + "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" +) + +# Apply the standard set of build settings. This can be removed for applications +# that need different build settings. +apply_standard_settings(${BINARY_NAME}) + +# Add preprocessor definitions for the application ID. +add_definitions(-DAPPLICATION_ID="${APPLICATION_ID}") + +# Add dependency libraries. Add any application-specific dependencies here. +target_link_libraries(${BINARY_NAME} PRIVATE flutter) +target_link_libraries(${BINARY_NAME} PRIVATE PkgConfig::GTK) + +target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}") diff --git a/examples/stac_gallery/linux/main.cc b/stac_playground/linux/runner/main.cc similarity index 100% rename from examples/stac_gallery/linux/main.cc rename to stac_playground/linux/runner/main.cc diff --git a/examples/stac_gallery/linux/my_application.cc b/stac_playground/linux/runner/my_application.cc similarity index 75% rename from examples/stac_gallery/linux/my_application.cc rename to stac_playground/linux/runner/my_application.cc index 30636248..f3994d72 100644 --- a/examples/stac_gallery/linux/my_application.cc +++ b/stac_playground/linux/runner/my_application.cc @@ -40,11 +40,11 @@ static void my_application_activate(GApplication* application) { if (use_header_bar) { GtkHeaderBar* header_bar = GTK_HEADER_BAR(gtk_header_bar_new()); gtk_widget_show(GTK_WIDGET(header_bar)); - gtk_header_bar_set_title(header_bar, "stac_gallery"); + gtk_header_bar_set_title(header_bar, "stac_playground"); gtk_header_bar_set_show_close_button(header_bar, TRUE); gtk_window_set_titlebar(window, GTK_WIDGET(header_bar)); } else { - gtk_window_set_title(window, "stac_gallery"); + gtk_window_set_title(window, "stac_playground"); } gtk_window_set_default_size(window, 1280, 720); @@ -81,6 +81,24 @@ static gboolean my_application_local_command_line(GApplication* application, gch return TRUE; } +// Implements GApplication::startup. +static void my_application_startup(GApplication* application) { + //MyApplication* self = MY_APPLICATION(object); + + // Perform any actions required at application startup. + + G_APPLICATION_CLASS(my_application_parent_class)->startup(application); +} + +// Implements GApplication::shutdown. +static void my_application_shutdown(GApplication* application) { + //MyApplication* self = MY_APPLICATION(object); + + // Perform any actions required at application shutdown. + + G_APPLICATION_CLASS(my_application_parent_class)->shutdown(application); +} + // Implements GObject::dispose. static void my_application_dispose(GObject* object) { MyApplication* self = MY_APPLICATION(object); @@ -91,12 +109,20 @@ static void my_application_dispose(GObject* object) { static void my_application_class_init(MyApplicationClass* klass) { G_APPLICATION_CLASS(klass)->activate = my_application_activate; G_APPLICATION_CLASS(klass)->local_command_line = my_application_local_command_line; + G_APPLICATION_CLASS(klass)->startup = my_application_startup; + G_APPLICATION_CLASS(klass)->shutdown = my_application_shutdown; G_OBJECT_CLASS(klass)->dispose = my_application_dispose; } static void my_application_init(MyApplication* self) {} MyApplication* my_application_new() { + // Set the program name to the application ID, which helps various systems + // like GTK and desktop environments map this running application to its + // corresponding .desktop file. This ensures better integration by allowing + // the application to be recognized beyond its binary name. + g_set_prgname(APPLICATION_ID); + return MY_APPLICATION(g_object_new(my_application_get_type(), "application-id", APPLICATION_ID, "flags", G_APPLICATION_NON_UNIQUE, diff --git a/examples/stac_gallery/linux/my_application.h b/stac_playground/linux/runner/my_application.h similarity index 100% rename from examples/stac_gallery/linux/my_application.h rename to stac_playground/linux/runner/my_application.h diff --git a/examples/stac_gallery/macos/.gitignore b/stac_playground/macos/.gitignore similarity index 100% rename from examples/stac_gallery/macos/.gitignore rename to stac_playground/macos/.gitignore diff --git a/examples/stac_gallery/macos/Flutter/Flutter-Debug.xcconfig b/stac_playground/macos/Flutter/Flutter-Debug.xcconfig similarity index 100% rename from examples/stac_gallery/macos/Flutter/Flutter-Debug.xcconfig rename to stac_playground/macos/Flutter/Flutter-Debug.xcconfig diff --git a/examples/stac_gallery/macos/Flutter/Flutter-Release.xcconfig b/stac_playground/macos/Flutter/Flutter-Release.xcconfig similarity index 100% rename from examples/stac_gallery/macos/Flutter/Flutter-Release.xcconfig rename to stac_playground/macos/Flutter/Flutter-Release.xcconfig diff --git a/examples/stac_gallery/macos/Flutter/GeneratedPluginRegistrant.swift b/stac_playground/macos/Flutter/GeneratedPluginRegistrant.swift similarity index 84% rename from examples/stac_gallery/macos/Flutter/GeneratedPluginRegistrant.swift rename to stac_playground/macos/Flutter/GeneratedPluginRegistrant.swift index 896f34b3..d3e3f154 100644 --- a/examples/stac_gallery/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/stac_playground/macos/Flutter/GeneratedPluginRegistrant.swift @@ -8,11 +8,13 @@ import Foundation import path_provider_foundation import shared_preferences_foundation import sqflite_darwin +import url_launcher_macos import webview_flutter_wkwebview func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin")) + UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin")) WebViewFlutterPlugin.register(with: registry.registrar(forPlugin: "WebViewFlutterPlugin")) } diff --git a/examples/stac_gallery/macos/Podfile b/stac_playground/macos/Podfile similarity index 100% rename from examples/stac_gallery/macos/Podfile rename to stac_playground/macos/Podfile diff --git a/examples/stac_gallery/macos/Runner.xcodeproj/project.pbxproj b/stac_playground/macos/Runner.xcodeproj/project.pbxproj similarity index 77% rename from examples/stac_gallery/macos/Runner.xcodeproj/project.pbxproj rename to stac_playground/macos/Runner.xcodeproj/project.pbxproj index fbf0e1a0..949fd9f4 100644 --- a/examples/stac_gallery/macos/Runner.xcodeproj/project.pbxproj +++ b/stac_playground/macos/Runner.xcodeproj/project.pbxproj @@ -21,14 +21,12 @@ /* End PBXAggregateTarget section */ /* Begin PBXBuildFile section */ - 21E27EFBDD0A58F14132E0CD /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C12C3AB7DF37E9D17816AA76 /* Pods_RunnerTests.framework */; }; 331C80D8294CF71000263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C80D7294CF71000263BE5 /* RunnerTests.swift */; }; 335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */ = {isa = PBXBuildFile; fileRef = 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */; }; 33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC10F02044A3C60003C045 /* AppDelegate.swift */; }; 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F22044A3C60003C045 /* Assets.xcassets */; }; 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F42044A3C60003C045 /* MainMenu.xib */; }; 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */; }; - CC72FE24F80A3C0AF471E121 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E625753ADF27D4CC0F4AF898 /* Pods_Runner.framework */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -62,12 +60,11 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ - 13755EAB6005DCD5D1615E18 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; 331C80D5294CF71000263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 331C80D7294CF71000263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = ""; }; 333000ED22D3DE5D00554162 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Warnings.xcconfig; sourceTree = ""; }; 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneratedPluginRegistrant.swift; sourceTree = ""; }; - 33CC10ED2044A3C60003C045 /* stac_gallery.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = stac_gallery.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 33CC10ED2044A3C60003C045 /* stac_playground.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "stac_playground.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 33CC10F02044A3C60003C045 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 33CC10F22044A3C60003C045 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = Runner/Assets.xcassets; sourceTree = ""; }; 33CC10F52044A3C60003C045 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = ""; }; @@ -79,15 +76,8 @@ 33E51913231747F40026EE4D /* DebugProfile.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DebugProfile.entitlements; sourceTree = ""; }; 33E51914231749380026EE4D /* Release.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = Release.entitlements; sourceTree = ""; }; 33E5194F232828860026EE4D /* AppInfo.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppInfo.xcconfig; sourceTree = ""; }; - 4CAED3DAFC94A56079659439 /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = ""; }; 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = ""; }; 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = ""; }; - AC60C042D493DFDD65E0FB8D /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; - C12C3AB7DF37E9D17816AA76 /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - C8A0F2FE0D2FD70769020992 /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = ""; }; - E2C6608AC1BC7B6050F8461F /* Pods-RunnerTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.profile.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.profile.xcconfig"; sourceTree = ""; }; - E625753ADF27D4CC0F4AF898 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - FBF6C2129F07C2620E1C2655 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -95,7 +85,6 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 21E27EFBDD0A58F14132E0CD /* Pods_RunnerTests.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -103,27 +92,12 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - CC72FE24F80A3C0AF471E121 /* Pods_Runner.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ - 09EAB53A41BF53FF8C7B6089 /* Pods */ = { - isa = PBXGroup; - children = ( - FBF6C2129F07C2620E1C2655 /* Pods-Runner.debug.xcconfig */, - 13755EAB6005DCD5D1615E18 /* Pods-Runner.release.xcconfig */, - AC60C042D493DFDD65E0FB8D /* Pods-Runner.profile.xcconfig */, - C8A0F2FE0D2FD70769020992 /* Pods-RunnerTests.debug.xcconfig */, - 4CAED3DAFC94A56079659439 /* Pods-RunnerTests.release.xcconfig */, - E2C6608AC1BC7B6050F8461F /* Pods-RunnerTests.profile.xcconfig */, - ); - name = Pods; - path = Pods; - sourceTree = ""; - }; 331C80D6294CF71000263BE5 /* RunnerTests */ = { isa = PBXGroup; children = ( @@ -151,14 +125,13 @@ 331C80D6294CF71000263BE5 /* RunnerTests */, 33CC10EE2044A3C60003C045 /* Products */, D73912EC22F37F3D000D13A0 /* Frameworks */, - 09EAB53A41BF53FF8C7B6089 /* Pods */, ); sourceTree = ""; }; 33CC10EE2044A3C60003C045 /* Products */ = { isa = PBXGroup; children = ( - 33CC10ED2044A3C60003C045 /* stac_gallery.app */, + 33CC10ED2044A3C60003C045 /* stac_playground.app */, 331C80D5294CF71000263BE5 /* RunnerTests.xctest */, ); name = Products; @@ -202,8 +175,6 @@ D73912EC22F37F3D000D13A0 /* Frameworks */ = { isa = PBXGroup; children = ( - E625753ADF27D4CC0F4AF898 /* Pods_Runner.framework */, - C12C3AB7DF37E9D17816AA76 /* Pods_RunnerTests.framework */, ); name = Frameworks; sourceTree = ""; @@ -215,7 +186,6 @@ isa = PBXNativeTarget; buildConfigurationList = 331C80DE294CF71000263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */; buildPhases = ( - FC682B1371BF9CD8324FEA17 /* [CP] Check Pods Manifest.lock */, 331C80D1294CF70F00263BE5 /* Sources */, 331C80D2294CF70F00263BE5 /* Frameworks */, 331C80D3294CF70F00263BE5 /* Resources */, @@ -234,13 +204,11 @@ isa = PBXNativeTarget; buildConfigurationList = 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */; buildPhases = ( - 2783DA6EE60A03874663898D /* [CP] Check Pods Manifest.lock */, 33CC10E92044A3C60003C045 /* Sources */, 33CC10EA2044A3C60003C045 /* Frameworks */, 33CC10EB2044A3C60003C045 /* Resources */, 33CC110E2044A8840003C045 /* Bundle Framework */, 3399D490228B24CF009A79C7 /* ShellScript */, - 7866210B3EA7701BEC7B9DC8 /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); @@ -249,7 +217,7 @@ ); name = Runner; productName = Runner; - productReference = 33CC10ED2044A3C60003C045 /* stac_gallery.app */; + productReference = 33CC10ED2044A3C60003C045 /* stac_playground.app */; productType = "com.apple.product-type.application"; }; /* End PBXNativeTarget section */ @@ -258,6 +226,7 @@ 33CC10E52044A3C60003C045 /* Project object */ = { isa = PBXProject; attributes = { + BuildIndependentTargetsInParallel = YES; LastSwiftUpdateCheck = 0920; LastUpgradeCheck = 1510; ORGANIZATIONNAME = ""; @@ -322,28 +291,6 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ - 2783DA6EE60A03874663898D /* [CP] Check Pods Manifest.lock */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", - ); - name = "[CP] Check Pods Manifest.lock"; - outputFileListPaths = ( - ); - outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; - showEnvVarsInLog = 0; - }; 3399D490228B24CF009A79C7 /* ShellScript */ = { isa = PBXShellScriptBuildPhase; alwaysOutOfDate = 1; @@ -382,45 +329,6 @@ shellPath = /bin/sh; shellScript = "\"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh && touch Flutter/ephemeral/tripwire"; }; - 7866210B3EA7701BEC7B9DC8 /* [CP] Embed Pods Frameworks */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", - ); - name = "[CP] Embed Pods Frameworks"; - outputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; - showEnvVarsInLog = 0; - }; - FC682B1371BF9CD8324FEA17 /* [CP] Check Pods Manifest.lock */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", - ); - name = "[CP] Check Pods Manifest.lock"; - outputFileListPaths = ( - ); - outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-RunnerTests-checkManifestLockResult.txt", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; - showEnvVarsInLog = 0; - }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ @@ -472,46 +380,43 @@ /* Begin XCBuildConfiguration section */ 331C80DB294CF71000263BE5 /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = C8A0F2FE0D2FD70769020992 /* Pods-RunnerTests.debug.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CURRENT_PROJECT_VERSION = 1; GENERATE_INFOPLIST_FILE = YES; MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = com.stac.stacGallery.RunnerTests; + PRODUCT_BUNDLE_IDENTIFIER = com.stac.stacPlayground.RunnerTests; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_VERSION = 5.0; - TEST_HOST = "$(BUILT_PRODUCTS_DIR)/stac_gallery.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/stac_gallery"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/stac_playground.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/stac_playground"; }; name = Debug; }; 331C80DC294CF71000263BE5 /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 4CAED3DAFC94A56079659439 /* Pods-RunnerTests.release.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CURRENT_PROJECT_VERSION = 1; GENERATE_INFOPLIST_FILE = YES; MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = com.stac.stacGallery.RunnerTests; + PRODUCT_BUNDLE_IDENTIFIER = com.stac.stacPlayground.RunnerTests; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_VERSION = 5.0; - TEST_HOST = "$(BUILT_PRODUCTS_DIR)/stac_gallery.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/stac_gallery"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/stac_playground.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/stac_playground"; }; name = Release; }; 331C80DD294CF71000263BE5 /* Profile */ = { isa = XCBuildConfiguration; - baseConfigurationReference = E2C6608AC1BC7B6050F8461F /* Pods-RunnerTests.profile.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CURRENT_PROJECT_VERSION = 1; GENERATE_INFOPLIST_FILE = YES; MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = com.stac.stacGallery.RunnerTests; + PRODUCT_BUNDLE_IDENTIFIER = com.stac.stacPlayground.RunnerTests; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_VERSION = 5.0; - TEST_HOST = "$(BUILT_PRODUCTS_DIR)/stac_gallery.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/stac_gallery"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/stac_playground.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/stac_playground"; }; name = Profile; }; @@ -520,6 +425,7 @@ baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; @@ -543,9 +449,11 @@ CLANG_WARN_SUSPICIOUS_MOVE = YES; CODE_SIGN_IDENTITY = "-"; COPY_PHASE_STRIP = NO; + DEAD_CODE_STRIPPING = YES; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_NO_COMMON_BLOCKS = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; @@ -593,6 +501,7 @@ baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; @@ -616,9 +525,11 @@ CLANG_WARN_SUSPICIOUS_MOVE = YES; CODE_SIGN_IDENTITY = "-"; COPY_PHASE_STRIP = NO; + DEAD_CODE_STRIPPING = YES; DEBUG_INFORMATION_FORMAT = dwarf; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_DYNAMIC_NO_PIC = NO; GCC_NO_COMMON_BLOCKS = YES; @@ -646,6 +557,7 @@ baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; @@ -669,9 +581,11 @@ CLANG_WARN_SUSPICIOUS_MOVE = YES; CODE_SIGN_IDENTITY = "-"; COPY_PHASE_STRIP = NO; + DEAD_CODE_STRIPPING = YES; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_NO_COMMON_BLOCKS = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; diff --git a/examples/stac_gallery/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/stac_playground/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist similarity index 100% rename from examples/stac_gallery/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist rename to stac_playground/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist diff --git a/examples/stac_gallery/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/stac_playground/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme similarity index 93% rename from examples/stac_gallery/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme rename to stac_playground/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index 285edaf1..de5b3ccd 100644 --- a/examples/stac_gallery/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/stac_playground/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -15,7 +15,7 @@ @@ -31,7 +31,7 @@ @@ -59,14 +59,13 @@ ignoresPersistentStateOnLaunch = "NO" debugDocumentVersioning = "YES" debugServiceExtension = "internal" - enableGPUValidationMode = "1" allowLocationSimulation = "YES"> @@ -83,7 +82,7 @@ diff --git a/examples/stac_gallery/macos/Runner.xcworkspace/contents.xcworkspacedata b/stac_playground/macos/Runner.xcworkspace/contents.xcworkspacedata similarity index 67% rename from examples/stac_gallery/macos/Runner.xcworkspace/contents.xcworkspacedata rename to stac_playground/macos/Runner.xcworkspace/contents.xcworkspacedata index 21a3cc14..1d526a16 100644 --- a/examples/stac_gallery/macos/Runner.xcworkspace/contents.xcworkspacedata +++ b/stac_playground/macos/Runner.xcworkspace/contents.xcworkspacedata @@ -4,7 +4,4 @@ - - diff --git a/examples/stac_gallery/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/stac_playground/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist similarity index 100% rename from examples/stac_gallery/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist rename to stac_playground/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist diff --git a/stac_playground/macos/Runner/AppDelegate.swift b/stac_playground/macos/Runner/AppDelegate.swift new file mode 100644 index 00000000..b3c17614 --- /dev/null +++ b/stac_playground/macos/Runner/AppDelegate.swift @@ -0,0 +1,13 @@ +import Cocoa +import FlutterMacOS + +@main +class AppDelegate: FlutterAppDelegate { + override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { + return true + } + + override func applicationSupportsSecureRestorableState(_ app: NSApplication) -> Bool { + return true + } +} diff --git a/examples/stac_gallery/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/stac_playground/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json similarity index 100% rename from examples/stac_gallery/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json rename to stac_playground/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json diff --git a/examples/stac_gallery/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png b/stac_playground/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png similarity index 100% rename from examples/stac_gallery/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png rename to stac_playground/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png diff --git a/examples/stac_gallery/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png b/stac_playground/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png similarity index 100% rename from examples/stac_gallery/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png rename to stac_playground/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png diff --git a/examples/stac_gallery/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png b/stac_playground/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png similarity index 100% rename from examples/stac_gallery/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png rename to stac_playground/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png diff --git a/examples/stac_gallery/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png b/stac_playground/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png similarity index 100% rename from examples/stac_gallery/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png rename to stac_playground/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png diff --git a/examples/stac_gallery/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png b/stac_playground/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png similarity index 100% rename from examples/stac_gallery/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png rename to stac_playground/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png diff --git a/examples/stac_gallery/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png b/stac_playground/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png similarity index 100% rename from examples/stac_gallery/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png rename to stac_playground/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png diff --git a/examples/stac_gallery/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png b/stac_playground/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png similarity index 100% rename from examples/stac_gallery/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png rename to stac_playground/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png diff --git a/examples/stac_gallery/macos/Runner/Base.lproj/MainMenu.xib b/stac_playground/macos/Runner/Base.lproj/MainMenu.xib similarity index 100% rename from examples/stac_gallery/macos/Runner/Base.lproj/MainMenu.xib rename to stac_playground/macos/Runner/Base.lproj/MainMenu.xib diff --git a/examples/stac_gallery/macos/Runner/Configs/AppInfo.xcconfig b/stac_playground/macos/Runner/Configs/AppInfo.xcconfig similarity index 75% rename from examples/stac_gallery/macos/Runner/Configs/AppInfo.xcconfig rename to stac_playground/macos/Runner/Configs/AppInfo.xcconfig index fe585831..7562bb0a 100644 --- a/examples/stac_gallery/macos/Runner/Configs/AppInfo.xcconfig +++ b/stac_playground/macos/Runner/Configs/AppInfo.xcconfig @@ -5,10 +5,10 @@ // 'flutter create' template. // The application's name. By default this is also the title of the Flutter window. -PRODUCT_NAME = stac_gallery +PRODUCT_NAME = stac_playground // The application's bundle identifier -PRODUCT_BUNDLE_IDENTIFIER = com.stac.stacGallery +PRODUCT_BUNDLE_IDENTIFIER = com.stac.stacPlayground // The copyright displayed in application information -PRODUCT_COPYRIGHT = Copyright © 2023 com.stac. All rights reserved. +PRODUCT_COPYRIGHT = Copyright © 2024 com.stac. All rights reserved. diff --git a/examples/stac_gallery/macos/Runner/Configs/Debug.xcconfig b/stac_playground/macos/Runner/Configs/Debug.xcconfig similarity index 100% rename from examples/stac_gallery/macos/Runner/Configs/Debug.xcconfig rename to stac_playground/macos/Runner/Configs/Debug.xcconfig diff --git a/examples/stac_gallery/macos/Runner/Configs/Release.xcconfig b/stac_playground/macos/Runner/Configs/Release.xcconfig similarity index 100% rename from examples/stac_gallery/macos/Runner/Configs/Release.xcconfig rename to stac_playground/macos/Runner/Configs/Release.xcconfig diff --git a/examples/stac_gallery/macos/Runner/Configs/Warnings.xcconfig b/stac_playground/macos/Runner/Configs/Warnings.xcconfig similarity index 100% rename from examples/stac_gallery/macos/Runner/Configs/Warnings.xcconfig rename to stac_playground/macos/Runner/Configs/Warnings.xcconfig diff --git a/examples/stac_gallery/macos/Runner/DebugProfile.entitlements b/stac_playground/macos/Runner/DebugProfile.entitlements similarity index 85% rename from examples/stac_gallery/macos/Runner/DebugProfile.entitlements rename to stac_playground/macos/Runner/DebugProfile.entitlements index c946719a..dddb8a30 100644 --- a/examples/stac_gallery/macos/Runner/DebugProfile.entitlements +++ b/stac_playground/macos/Runner/DebugProfile.entitlements @@ -8,7 +8,5 @@ com.apple.security.network.server - com.apple.security.network.client - diff --git a/examples/stac_gallery/macos/Runner/Info.plist b/stac_playground/macos/Runner/Info.plist similarity index 100% rename from examples/stac_gallery/macos/Runner/Info.plist rename to stac_playground/macos/Runner/Info.plist diff --git a/stac_playground/macos/Runner/MainFlutterWindow.swift b/stac_playground/macos/Runner/MainFlutterWindow.swift new file mode 100644 index 00000000..3cc05eb2 --- /dev/null +++ b/stac_playground/macos/Runner/MainFlutterWindow.swift @@ -0,0 +1,15 @@ +import Cocoa +import FlutterMacOS + +class MainFlutterWindow: NSWindow { + override func awakeFromNib() { + let flutterViewController = FlutterViewController() + let windowFrame = self.frame + self.contentViewController = flutterViewController + self.setFrame(windowFrame, display: true) + + RegisterGeneratedPlugins(registry: flutterViewController) + + super.awakeFromNib() + } +} diff --git a/examples/stac_gallery/macos/Runner/Release.entitlements b/stac_playground/macos/Runner/Release.entitlements similarity index 80% rename from examples/stac_gallery/macos/Runner/Release.entitlements rename to stac_playground/macos/Runner/Release.entitlements index 48271acc..852fa1a4 100644 --- a/examples/stac_gallery/macos/Runner/Release.entitlements +++ b/stac_playground/macos/Runner/Release.entitlements @@ -4,7 +4,5 @@ com.apple.security.app-sandbox - com.apple.security.network.client - diff --git a/stac_playground/macos/RunnerTests/RunnerTests.swift b/stac_playground/macos/RunnerTests/RunnerTests.swift new file mode 100644 index 00000000..61f3bd1f --- /dev/null +++ b/stac_playground/macos/RunnerTests/RunnerTests.swift @@ -0,0 +1,12 @@ +import Cocoa +import FlutterMacOS +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/pubspec.lock b/stac_playground/pubspec.lock similarity index 62% rename from examples/stac_gallery/pubspec.lock rename to stac_playground/pubspec.lock index 5770e792..9be53ca6 100644 --- a/examples/stac_gallery/pubspec.lock +++ b/stac_playground/pubspec.lock @@ -1,102 +1,38 @@ # Generated by pub # See https://dart.dev/tools/pub/glossary#lockfile packages: - _fe_analyzer_shared: - dependency: transitive - description: - name: _fe_analyzer_shared - sha256: "8d7ff3948166b8ec5da0fbb5962000926b8e02f2ed9b3e51d1738905fbd4c98d" - url: "https://pub.dev" - source: hosted - version: "93.0.0" - analyzer: - dependency: transitive - description: - name: analyzer - sha256: de7148ed2fcec579b19f122c1800933dfa028f6d9fd38a152b04b1516cec120b - url: "https://pub.dev" - source: hosted - version: "10.0.1" args: dependency: transitive description: name: args - sha256: d0481093c50b1da8910eb0bb301626d4d8eb7284aa739614d2b394ee09e3ea04 + sha256: bf9f5caeea8d8fe6721a9c358dd8a5c1947b27f1cfaa18b39c301273594919e6 url: "https://pub.dev" source: hosted - version: "2.7.0" + version: "2.6.0" async: dependency: transitive description: name: async - sha256: "758e6d74e971c3e5aceb4110bfd6698efc7f501675bcfe0c775459a8140750eb" + sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" url: "https://pub.dev" source: hosted - version: "2.13.0" + version: "2.11.0" bloc: dependency: transitive description: name: bloc - sha256: "52c10575f4445c61dd9e0cafcc6356fdd827c4c64dd7945ef3c4105f6b6ac189" + sha256: "106842ad6569f0b60297619e9e0b1885c2fb9bf84812935490e6c5275777804e" url: "https://pub.dev" source: hosted - version: "9.0.0" + version: "8.1.4" boolean_selector: dependency: transitive description: name: boolean_selector - sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea" - url: "https://pub.dev" - source: hosted - version: "2.1.2" - build: - dependency: transitive - description: - name: build - sha256: "275bf6bb2a00a9852c28d4e0b410da1d833a734d57d39d44f94bfc895a484ec3" - url: "https://pub.dev" - source: hosted - version: "4.0.4" - build_config: - dependency: transitive - description: - name: build_config - sha256: "4f64382b97504dc2fcdf487d5aae33418e08b4703fc21249e4db6d804a4d0187" - url: "https://pub.dev" - source: hosted - version: "1.2.0" - build_daemon: - dependency: transitive - description: - name: build_daemon - sha256: "8e928697a82be082206edb0b9c99c5a4ad6bc31c9e9b8b2f291ae65cd4a25daa" - url: "https://pub.dev" - source: hosted - version: "4.0.4" - build_runner: - dependency: "direct dev" - description: - name: build_runner - sha256: "39ad4ca8a2876779737c60e4228b4bcd35d4352ef7e14e47514093edc012c734" - url: "https://pub.dev" - source: hosted - version: "2.11.1" - built_collection: - dependency: transitive - description: - name: built_collection - sha256: "376e3dd27b51ea877c28d525560790aee2e6fbb5f20e2f85d5081027d94e2100" - url: "https://pub.dev" - source: hosted - version: "5.1.1" - built_value: - dependency: transitive - description: - name: built_value - sha256: "7931c90b84bc573fef103548e354258ae4c9d28d140e41961df6843c5d60d4d8" + sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" url: "https://pub.dev" source: hosted - version: "8.12.3" + version: "2.1.1" cached_network_image: dependency: transitive description: @@ -129,14 +65,6 @@ packages: url: "https://pub.dev" source: hosted version: "1.4.1" - checked_yaml: - dependency: transitive - description: - name: checked_yaml - sha256: feb6bed21949061731a7a75fc5d2aa727cf160b91af9a3e464c5e3a32e28b5ff - url: "https://pub.dev" - source: hosted - version: "2.0.3" clock: dependency: transitive description: @@ -145,14 +73,6 @@ packages: url: "https://pub.dev" source: hosted version: "1.1.2" - code_builder: - dependency: transitive - description: - name: code_builder - sha256: "0ec10bf4a89e4c613960bf1e8b42c64127021740fb21640c29c909826a5eea3e" - url: "https://pub.dev" - source: hosted - version: "4.10.1" collection: dependency: transitive description: @@ -161,14 +81,6 @@ packages: url: "https://pub.dev" source: hosted version: "1.19.1" - convert: - dependency: transitive - description: - name: convert - sha256: b30acd5944035672bc15c6b7a8b47d773e41e2f17de064350988c5d02adb1c68 - url: "https://pub.dev" - source: hosted - version: "3.1.2" crypto: dependency: transitive description: @@ -185,30 +97,22 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.8" - dart_style: - dependency: transitive - description: - name: dart_style - sha256: "15a7db352c8fc6a4d2bc475ba901c25b39fe7157541da4c16eacce6f8be83e49" - url: "https://pub.dev" - source: hosted - version: "3.1.5" dio: dependency: transitive description: name: dio - sha256: d90ee57923d1828ac14e492ca49440f65477f4bb1263575900be731a3dac66a9 + sha256: aff32c08f92787a557dd5c0145ac91536481831a01b4648136373cddb0e64f8c url: "https://pub.dev" source: hosted - version: "5.9.0" + version: "5.9.2" dio_web_adapter: dependency: transitive description: name: dio_web_adapter - sha256: "7586e476d70caecaf1686d21eee7247ea43ef5c345eab9e0cc3583ff13378d78" + sha256: "2f9e64323a7c3c7ef69567d5c800424a11f8337b8b228bad02524c9fb3c1f340" url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "2.1.2" fake_async: dependency: transitive description: @@ -221,10 +125,10 @@ packages: dependency: transitive description: name: ffi - sha256: "289279317b4b16eb2bb7e271abccd4bf84ec9bdcbe999e278a94b804f5630418" + sha256: "16ed7b077ef01ad6170a3d0c57caa4a112a38d7a2ed5602e0aca9ca6f3d98da6" url: "https://pub.dev" source: hosted - version: "2.1.4" + version: "2.1.3" file: dependency: transitive description: @@ -250,10 +154,10 @@ packages: dependency: "direct main" description: name: flutter_bloc - sha256: cf51747952201a455a1c840f8171d273be009b932c75093020f9af64f2123e38 + sha256: b594505eac31a0518bdcb4b5b79573b8d9117b193cc80cc12e17d639b10aa27a url: "https://pub.dev" source: hosted - version: "9.1.1" + version: "8.1.6" flutter_cache_manager: dependency: transitive description: @@ -271,13 +175,13 @@ packages: source: hosted version: "5.0.0" flutter_svg: - dependency: transitive + dependency: "direct main" description: name: flutter_svg - sha256: "87fbd7c534435b6c5d9d98b01e1fd527812b82e68ddd8bd35fc45ed0fa8f0a95" + sha256: "1ded017b39c8e15c8948ea855070a5ff8ff8b3d5e83f3446e02d6bb12add7ad9" url: "https://pub.dev" source: hosted - version: "2.2.3" + version: "2.2.4" flutter_test: dependency: "direct dev" description: flutter @@ -296,102 +200,62 @@ packages: description: flutter source: sdk version: "0.0.0" - freezed: - dependency: "direct dev" - description: - name: freezed - sha256: f23ea33b3863f119b58ed1b586e881a46bd28715ddcc4dbc33104524e3434131 - url: "https://pub.dev" - source: hosted - version: "3.2.5" - freezed_annotation: + google_fonts: dependency: "direct main" description: - name: freezed_annotation - sha256: "7294967ff0a6d98638e7acb774aac3af2550777accd8149c90af5b014e6d44d8" + name: google_fonts + sha256: db9df7a5898d894eeda4c78143f35c30a243558be439518972366880b80bf88e url: "https://pub.dev" source: hosted - version: "3.1.0" - glob: - dependency: transitive - description: - name: glob - sha256: c3f1ee72c96f8f78935e18aa8cecced9ab132419e8625dc187e1c2408efc20de - url: "https://pub.dev" - source: hosted - version: "2.1.3" - graphs: - dependency: transitive - description: - name: graphs - sha256: "741bbf84165310a68ff28fe9e727332eef1407342fca52759cb21ad8177bb8d0" - url: "https://pub.dev" - source: hosted - version: "2.3.2" + version: "8.0.2" http: dependency: transitive description: name: http - sha256: "2c11f3f94c687ee9bad77c171151672986360b2b001d109814ee7140b2cf261b" + sha256: b9c29a161230ee03d3ccf545097fccd9b87a5264228c5d348202e0f0c28f9010 url: "https://pub.dev" source: hosted - version: "1.4.0" - http_multi_server: - dependency: transitive - description: - name: http_multi_server - sha256: aa6199f908078bb1c5efb8d8638d4ae191aac11b311132c3ef48ce352fb52ef8 - url: "https://pub.dev" - source: hosted - version: "3.2.2" + version: "1.2.2" http_parser: dependency: transitive description: name: http_parser - sha256: "178d74305e7866013777bab2c3d8726205dc5a4dd935297175b19a23a2e66571" + sha256: "76d306a1c3afb33fe82e2bbacad62a61f409b5634c915fceb0d799de1a913360" url: "https://pub.dev" source: hosted - version: "4.1.2" - io: + version: "4.1.1" + isolate_contactor: dependency: transitive description: - name: io - sha256: dfd5a80599cf0165756e3181807ed3e77daf6dd4137caaad72d0b7931597650b - url: "https://pub.dev" - source: hosted - version: "1.0.5" - json_annotation: - dependency: "direct main" - description: - name: json_annotation - sha256: "805fa86df56383000f640384b282ce0cb8431f1a7a2396de92fb66186d8c57df" + name: isolate_contactor + sha256: "6ba8434ceb58238a1389d6365111a3efe7baa1c68a66f4db6d63d351cf6c3a0f" url: "https://pub.dev" source: hosted - version: "4.10.0" - json_schema: + version: "4.1.0" + isolate_manager: dependency: transitive description: - name: json_schema - sha256: f37d9c3fdfe8c9aae55fdfd5af815d24ce63c3a0f6a2c1f0982c30f43643fa1a + name: isolate_manager + sha256: "22ed0c25f80ec3b5f21e3a55d060f4650afff33f27c2dff34c0f9409d5759ae5" url: "https://pub.dev" source: hosted - version: "5.2.2" - json_serializable: - dependency: "direct dev" + version: "4.1.5+1" + json_annotation: + dependency: transitive description: - name: json_serializable - sha256: "93fba3ad139dab2b1ce59ecc6fdce6da46a42cdb6c4399ecda30f1e7e725760d" + name: json_annotation + sha256: "2a743920d81b7910627f68ee2c9ac1fc0bfee32b9fc3403587d7c6791ca12f80" url: "https://pub.dev" source: hosted - version: "6.12.0" + version: "4.12.0" leak_tracker: dependency: transitive description: name: leak_tracker - sha256: "8dcda04c3fc16c14f48a7bb586d4be1f0d1572731b6d81d51772ef47c02081e0" + sha256: "33e2e26bdd85a0112ec15400c8cbffea70d0f9c3407491f672a2fad47915e2de" url: "https://pub.dev" source: hosted - version: "11.0.1" + version: "11.0.2" leak_tracker_flutter_testing: dependency: transitive description: @@ -417,21 +281,13 @@ packages: source: hosted version: "5.1.1" logger: - dependency: transitive + dependency: "direct main" description: name: logger - sha256: be4b23575aac7ebf01f225a241eb7f6b5641eeaf43c6a8613510fc2f8cf187d1 + sha256: "7ad7215c15420a102ec687bb320a7312afd449bac63bfb1c60d9787c27b9767f" url: "https://pub.dev" source: hosted - version: "2.5.0" - logging: - dependency: transitive - description: - name: logging - sha256: c8245ada5f1717ed44271ed1c26b8ce85ca3228fd2ffdb75468ab01979309d61 - url: "https://pub.dev" - source: hosted - version: "1.3.0" + version: "1.4.0" matcher: dependency: transitive description: @@ -480,14 +336,6 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.0" - package_config: - dependency: transitive - description: - name: package_config - sha256: f096c55ebb7deb7e384101542bfba8c52696c1b56fca2eb62827989ef2353bbc - url: "https://pub.dev" - source: hosted - version: "2.2.0" path: dependency: transitive description: @@ -516,10 +364,10 @@ packages: dependency: transitive description: name: path_provider_android - sha256: d0d310befe2c8ab9e7f393288ccbb11b60c019c6b5afc21973eeee4dda2b35e9 + sha256: "4adf4fd5423ec60a29506c76581bc05854c55e3a0b72d35bb28d661c9686edf2" url: "https://pub.dev" source: hosted - version: "2.2.17" + version: "2.2.15" path_provider_foundation: dependency: transitive description: @@ -556,10 +404,18 @@ packages: dependency: transitive description: name: petitparser - sha256: "07c8f0b1913bcde1ff0d26e57ace2f3012ccbf2b204e070290dad3bb22797646" + sha256: c15605cd28af66339f8eb6fbe0e541bfe2d1b72d5825efc6598f3e0a31b9ad27 + url: "https://pub.dev" + source: hosted + version: "6.0.2" + phosphoricons_flutter: + dependency: "direct main" + description: + name: phosphoricons_flutter + sha256: "34e4a00cc1a4be87c60edfcfcd5de23c005dc69945a5d77e08d33e6d23c0e793" url: "https://pub.dev" source: hosted - version: "6.1.0" + version: "1.0.0" platform: dependency: transitive description: @@ -576,54 +432,30 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.8" - pool: - dependency: transitive - description: - name: pool - sha256: "20fe868b6314b322ea036ba325e6fc0711a22948856475e2c2b6306e8ab39c2a" - url: "https://pub.dev" - source: hosted - version: "1.5.1" provider: dependency: transitive description: name: provider - sha256: "4abbd070a04e9ddc287673bf5a030c7ca8b685ff70218720abab8b092f53dd84" - url: "https://pub.dev" - source: hosted - version: "6.1.5" - pub_semver: - dependency: transitive - description: - name: pub_semver - sha256: "5bfcf68ca79ef689f8990d1160781b4bad40a3bd5e5218ad4076ddb7f4081585" + sha256: c8a055ee5ce3fd98d6fc872478b03823ffdb448699c6ebdbbc71d59b596fd48c url: "https://pub.dev" source: hosted - version: "2.2.0" - pubspec_parse: - dependency: transitive - description: - name: pubspec_parse - sha256: "0560ba233314abbed0a48a2956f7f022cce7c3e1e73df540277da7544cad4082" - url: "https://pub.dev" - source: hosted - version: "1.5.0" - quiver: - dependency: transitive + version: "6.1.2" + re_editor: + dependency: "direct main" description: - name: quiver - sha256: ea0b925899e64ecdfbf9c7becb60d5b50e706ade44a85b2363be2a22d88117d2 + name: re_editor + sha256: "66671c4774a6b4c5254c9a53ab35a083e7e7da9ae371c519bcf491c70a2a4e56" url: "https://pub.dev" source: hosted - version: "3.2.2" - rfc_6901: - dependency: transitive + version: "0.10.0" + re_highlight: + dependency: "direct main" description: - name: rfc_6901 - sha256: "6a43b1858dca2febaf93e15639aa6b0c49ccdfd7647775f15a499f872b018154" + name: re_highlight + sha256: "6c4ac3f76f939fb7ca9df013df98526634e17d8f7460e028bd23a035870024f2" url: "https://pub.dev" source: hosted - version: "0.2.1" + version: "0.0.3" rxdart: dependency: transitive description: @@ -636,18 +468,18 @@ packages: dependency: transitive description: name: shared_preferences - sha256: "2939ae520c9024cb197fc20dee269cd8cdbf564c8b5746374ec6cacdc5169e64" + sha256: c3025c5534b01739267eb7d76959bbc25a6d10f6988e1c2a3036940133dd10bf url: "https://pub.dev" source: hosted - version: "2.5.4" + version: "2.5.5" shared_preferences_android: dependency: transitive description: name: shared_preferences_android - sha256: "07d552dbe8e71ed720e5205e760438ff4ecfb76ec3b32ea664350e2ca4b0c43b" + sha256: e8d4762b1e2e8578fc4d0fd548cebf24afd24f49719c08974df92834565e2c53 url: "https://pub.dev" source: hosted - version: "2.4.16" + version: "2.4.23" shared_preferences_foundation: dependency: transitive description: @@ -668,10 +500,10 @@ packages: dependency: transitive description: name: shared_preferences_platform_interface - sha256: "57cbf196c486bc2cf1f02b85784932c6094376284b3ad5779d1b1c6c6a816b80" + sha256: "649dc798a33931919ea356c4305c2d1f81619ea6e92244070b520187b5140ef9" url: "https://pub.dev" source: hosted - version: "2.4.1" + version: "2.4.2" shared_preferences_web: dependency: transitive description: @@ -688,59 +520,19 @@ packages: url: "https://pub.dev" source: hosted version: "2.4.1" - shelf: - dependency: transitive - description: - name: shelf - sha256: e7dd780a7ffb623c57850b33f43309312fc863fb6aa3d276a754bb299839ef12 - url: "https://pub.dev" - source: hosted - version: "1.4.2" - shelf_web_socket: - dependency: transitive - description: - name: shelf_web_socket - sha256: "3632775c8e90d6c9712f883e633716432a27758216dfb61bd86a8321c0580925" - url: "https://pub.dev" - source: hosted - version: "3.0.0" sky_engine: dependency: transitive description: flutter source: sdk version: "0.0.0" - source_gen: - dependency: transitive - description: - name: source_gen - sha256: "1d562a3c1f713904ebbed50d2760217fd8a51ca170ac4b05b0db490699dbac17" - url: "https://pub.dev" - source: hosted - version: "4.2.0" - source_helper: - dependency: transitive - description: - name: source_helper - sha256: "4a85e90b50694e652075cbe4575665539d253e6ec10e46e76b45368ab5e3caae" - url: "https://pub.dev" - source: hosted - version: "1.3.10" source_span: dependency: transitive description: name: source_span - sha256: "254ee5351d6cb365c859e20ee823c3bb479bf4a293c22d17a9f1bf144ce86f7c" + sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" url: "https://pub.dev" source: hosted - version: "1.10.1" - sprintf: - dependency: transitive - description: - name: sprintf - sha256: "1fc9ffe69d4df602376b52949af107d8f5703b77cda567c4d7d86a0693120f23" - url: "https://pub.dev" - source: hosted - version: "7.0.0" + version: "1.10.0" sqflite: dependency: transitive description: @@ -753,18 +545,18 @@ packages: dependency: transitive description: name: sqflite_android - sha256: "2b3070c5fa881839f8b402ee4a39c1b4d561704d4ebbbcfb808a119bc2a1701b" + sha256: "881e28efdcc9950fd8e9bb42713dcf1103e62a2e7168f23c9338d82db13dec40" url: "https://pub.dev" source: hosted - version: "2.4.1" + version: "2.4.2+3" sqflite_common: dependency: transitive description: name: sqflite_common - sha256: "84731e8bfd8303a3389903e01fb2141b6e59b5973cacbb0929021df08dddbe8b" + sha256: "6ef422a4525ecc601db6c0a2233ff448c731307906e92cabc9ba292afaae16a6" url: "https://pub.dev" source: hosted - version: "2.5.5" + version: "2.5.6" sqflite_darwin: dependency: transitive description: @@ -784,35 +576,35 @@ packages: stac: dependency: "direct main" description: - path: "../../packages/stac" + path: "../packages/stac" relative: true source: path version: "1.5.0" stac_core: - dependency: "direct overridden" + dependency: "direct main" description: - path: "../../packages/stac_core" + path: "../packages/stac_core" relative: true source: path version: "1.5.0" stac_framework: dependency: "direct overridden" description: - path: "../../packages/stac_framework" + path: "../packages/stac_framework" relative: true source: path version: "1.0.0" stac_logger: dependency: "direct overridden" description: - path: "../../packages/stac_logger" + path: "../packages/stac_logger" relative: true source: path version: "1.1.0" stac_webview: dependency: "direct main" description: - path: "../../packages/stac_webview" + path: "../packages/stac_webview" relative: true source: path version: "1.0.0" @@ -832,38 +624,30 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.4" - stream_transform: - dependency: transitive - description: - name: stream_transform - sha256: ad47125e588cfd37a9a7f86c7d6356dde8dfe89d071d293f80ca9e9273a33871 - url: "https://pub.dev" - source: hosted - version: "2.1.1" string_scanner: dependency: transitive description: name: string_scanner - sha256: "921cd31725b72fe181906c6a94d987c78e3b98c2e205b397ea399d4054872b43" + sha256: "688af5ed3402a4bde5b3a6c15fd768dbf2621a614950b17f04626c431ab3c4c3" url: "https://pub.dev" source: hosted - version: "1.4.1" + version: "1.3.0" synchronized: dependency: transitive description: name: synchronized - sha256: "0669c70faae6270521ee4f05bffd2919892d42d1276e6c495be80174b6bc0ef6" + sha256: c254ade258ec8282947a0acbbc90b9575b4f19673533ee46f2f6e9b3aeefd7c0 url: "https://pub.dev" source: hosted - version: "3.3.1" + version: "3.4.0" term_glyph: dependency: transitive description: name: term_glyph - sha256: "7f554798625ea768a7518313e58f83891c7f5024f88e46e7182a4558850a4b8e" + sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 url: "https://pub.dev" source: hosted - version: "1.2.2" + version: "1.2.1" test_api: dependency: transitive description: @@ -880,46 +664,102 @@ packages: url: "https://pub.dev" source: hosted version: "1.4.0" - uri: + url_launcher: + dependency: "direct main" + description: + name: url_launcher + sha256: "9d06212b1362abc2f0f0d78e6f09f726608c74e3b9462e8368bb03314aa8d603" + url: "https://pub.dev" + source: hosted + version: "6.3.1" + url_launcher_android: dependency: transitive description: - name: uri - sha256: "889eea21e953187c6099802b7b4cf5219ba8f3518f604a1033064d45b1b8268a" + name: url_launcher_android + sha256: "6fc2f56536ee873eeb867ad176ae15f304ccccc357848b351f6f0d8d4a40d193" url: "https://pub.dev" source: hosted - version: "1.0.0" + version: "6.3.14" + url_launcher_ios: + dependency: transitive + description: + name: url_launcher_ios + sha256: "16a513b6c12bb419304e72ea0ae2ab4fed569920d1c7cb850263fe3acc824626" + url: "https://pub.dev" + source: hosted + version: "6.3.2" + url_launcher_linux: + dependency: transitive + description: + name: url_launcher_linux + sha256: "4e9ba368772369e3e08f231d2301b4ef72b9ff87c31192ef471b380ef29a4935" + url: "https://pub.dev" + source: hosted + version: "3.2.1" + url_launcher_macos: + dependency: transitive + description: + name: url_launcher_macos + sha256: "17ba2000b847f334f16626a574c702b196723af2a289e7a93ffcb79acff855c2" + url: "https://pub.dev" + source: hosted + version: "3.2.2" + url_launcher_platform_interface: + dependency: transitive + description: + name: url_launcher_platform_interface + sha256: "552f8a1e663569be95a8190206a38187b531910283c3e982193e4f2733f01029" + url: "https://pub.dev" + source: hosted + version: "2.3.2" + url_launcher_web: + dependency: transitive + description: + name: url_launcher_web + sha256: "772638d3b34c779ede05ba3d38af34657a05ac55b06279ea6edd409e323dca8e" + url: "https://pub.dev" + source: hosted + version: "2.3.3" + url_launcher_windows: + dependency: transitive + description: + name: url_launcher_windows + sha256: "44cf3aabcedde30f2dba119a9dea3b0f2672fbe6fa96e85536251d678216b3c4" + url: "https://pub.dev" + source: hosted + version: "3.1.3" uuid: dependency: transitive description: name: uuid - sha256: a5be9ef6618a7ac1e964353ef476418026db906c4facdedaa299b7a2e71690ff + sha256: "1fef9e8e11e2991bb773070d4656b7bd5d850967a2456cfc83cf47925ba79489" url: "https://pub.dev" source: hosted - version: "4.5.1" + version: "4.5.3" vector_graphics: dependency: transitive description: name: vector_graphics - sha256: "44cc7104ff32563122a929e4620cf3efd584194eec6d1d913eb5ba593dbcf6de" + sha256: "27d5fefe86fb9aace4a9f8375b56b3c292b64d8c04510df230f849850d912cb7" url: "https://pub.dev" source: hosted - version: "1.1.18" + version: "1.1.15" vector_graphics_codec: dependency: transitive description: name: vector_graphics_codec - sha256: "99fd9fbd34d9f9a32efd7b6a6aae14125d8237b10403b422a6a6dfeac2806146" + sha256: "2430b973a4ca3c4dbc9999b62b8c719a160100dcbae5c819bae0cacce32c9cdb" url: "https://pub.dev" source: hosted - version: "1.1.13" + version: "1.1.12" vector_graphics_compiler: dependency: transitive description: name: vector_graphics_compiler - sha256: "557a315b7d2a6dbb0aaaff84d857967ce6bdc96a63dc6ee2a57ce5a6ee5d3331" + sha256: "1b4b9e706a10294258727674a340ae0d6e64a7231980f9f9a3d12e4b42407aad" url: "https://pub.dev" source: hosted - version: "1.1.17" + version: "1.1.16" vector_math: dependency: transitive description: @@ -932,74 +772,50 @@ packages: dependency: transitive description: name: vm_service - sha256: ddfa8d30d89985b96407efce8acbdd124701f96741f2d981ca860662f1c0dc02 + sha256: f6be3ed8bd01289b34d679c2b62226f63c0e69f9fd2e50a6b3c1c729a961041b url: "https://pub.dev" source: hosted - version: "15.0.0" - watcher: - dependency: transitive - description: - name: watcher - sha256: "69da27e49efa56a15f8afe8f4438c4ec02eff0a117df1b22ea4aad194fe1c104" - url: "https://pub.dev" - source: hosted - version: "1.1.1" + version: "14.3.0" web: - dependency: transitive + dependency: "direct main" description: name: web sha256: "868d88a33d8a87b18ffc05f9f030ba328ffefba92d6c127917a2ba740f9cfe4a" url: "https://pub.dev" source: hosted version: "1.1.1" - web_socket: - dependency: transitive - description: - name: web_socket - sha256: "34d64019aa8e36bf9842ac014bb5d2f5586ca73df5e4d9bf5c936975cae6982c" - url: "https://pub.dev" - source: hosted - version: "1.0.1" - web_socket_channel: - dependency: transitive - description: - name: web_socket_channel - sha256: d645757fb0f4773d602444000a8131ff5d48c9e47adfe9772652dd1a4f2d45c8 - url: "https://pub.dev" - source: hosted - version: "3.0.3" webview_flutter: dependency: transitive description: name: webview_flutter - sha256: a3da219916aba44947d3a5478b1927876a09781174b5a2b67fa5be0555154bf9 + sha256: d53e1ccf5516f25017e3c9d44c39034db352d20fa34fe200674270242c2c5111 url: "https://pub.dev" source: hosted - version: "4.13.1" + version: "4.14.1" webview_flutter_android: dependency: transitive description: name: webview_flutter_android - sha256: f6e6afef6e234801da77170f7a1847ded8450778caf2fe13979d140484be3678 + sha256: a97db7a44f8e71af2f3971c45550a08cce1fb60059c1b8e534251e6cfb753490 url: "https://pub.dev" source: hosted - version: "4.7.0" + version: "4.13.0" webview_flutter_platform_interface: dependency: transitive description: name: webview_flutter_platform_interface - sha256: "7cb32b21825bd65569665c32bb00a34ded5779786d6201f5350979d2d529940d" + sha256: "1221c1b12f5278791042f2ec2841743784cf25c5a644e23d6680e5d718824f04" url: "https://pub.dev" source: hosted - version: "2.13.0" + version: "2.15.1" webview_flutter_wkwebview: dependency: transitive description: name: webview_flutter_wkwebview - sha256: a3d461fe3467014e05f3ac4962e5fdde2a4bf44c561cb53e9ae5c586600fdbc3 + sha256: c879dd64b87c452aa84381b244d5469da57ba7e8cca6884c7b1e0d406372c12d url: "https://pub.dev" source: hosted - version: "3.22.0" + version: "3.26.0" xdg_directories: dependency: transitive description: @@ -1016,14 +832,6 @@ packages: url: "https://pub.dev" source: hosted version: "6.5.0" - yaml: - dependency: transitive - description: - name: yaml - sha256: b9da305ac7c39faa3f030eccd175340f968459dae4af175130b3fc47e40d76ce - url: "https://pub.dev" - source: hosted - version: "3.1.3" sdks: - dart: ">=3.10.0-0 <4.0.0" - flutter: ">=3.35.0" + dart: ">=3.12.0 <4.0.0" + flutter: ">=3.44.0" diff --git a/examples/stac_gallery/pubspec.yaml b/stac_playground/pubspec.yaml similarity index 54% rename from examples/stac_gallery/pubspec.yaml rename to stac_playground/pubspec.yaml index ea943f9a..2d332cdd 100644 --- a/examples/stac_gallery/pubspec.yaml +++ b/stac_playground/pubspec.yaml @@ -1,8 +1,8 @@ -name: stac_gallery -description: A Stac gallery app to showcase all the Stac widgets +name: stac_playground +description: "Stac Playground" # The following line prevents the package from being accidentally published to # pub.dev using `flutter pub publish`. This is preferred for private packages. -publish_to: "none" # Remove this line if you wish to publish to pub.dev +publish_to: 'none' # Remove this line if you wish to publish to pub.dev # The following defines the version and build number for your application. # A version number is three numbers separated by dots, like 1.2.43 @@ -19,7 +19,7 @@ publish_to: "none" # Remove this line if you wish to publish to pub.dev version: 1.0.0+1 environment: - sdk: ">=3.0.0 <4.0.0" + sdk: ^3.6.0 # Dependencies specify other packages that your package needs in order to work. # To automatically upgrade your package dependencies to the latest versions @@ -33,13 +33,28 @@ dependencies: # The following adds the Cupertino Icons font to your application. # Use with the CupertinoIcons class for iOS style icons. - cupertino_icons: ^1.0.6 - freezed_annotation: ^3.0.0 - json_annotation: ^4.9.0 - flutter_bloc: ^9.1.0 + cupertino_icons: ^1.0.8 + logger: ^2.5.0 stac: + path: ../packages/stac + stac_core: ^1.5.0 stac_webview: + path: ../packages/stac_webview + re_editor: ^0.10.0 + re_highlight: ^0.0.3 + # phosphor_flutter 2.1.0 extends IconData, which is a final class as of + # Flutter 3.44, so the icons come from phosphoricons_flutter instead. + phosphoricons_flutter: ^1.0.0 + url_launcher: ^6.3.1 + flutter_svg: ^2.0.16 + flutter_bloc: ^8.1.6 + google_fonts: ^8.0.2 + web: ^1.1.1 +dependency_overrides: + logger: ^1.1.0 + stac_core: + path: ../packages/stac_core dev_dependencies: flutter_test: @@ -51,16 +66,55 @@ dev_dependencies: # package. See that file for information about deactivating specific lint # rules and activating additional ones. flutter_lints: ^5.0.0 - build_runner: ^2.4.15 - freezed: ^3.0.6 - json_serializable: ^6.9.4 # For information on the generic Dart part of this file, see the # following page: https://dart.dev/tools/pub/pubspec # The following section is specific to Flutter packages. flutter: + + # The following line ensures that the Material Icons font is + # included with your application, so that you can use the icons in + # the material Icons class. uses-material-design: true + assets: - assets/images/ - assets/json/ + - lib/dsl/ + + fonts: + - family: Figtree + fonts: + - asset: assets/fonts/Figtree.ttf + + # To add assets to your application, add an assets section, like this: + # assets: + # - images/a_dot_burr.jpeg + # - images/a_dot_ham.jpeg + + # An image asset can refer to one or more resolution-specific "variants", see + # https://flutter.dev/to/resolution-aware-images + + # For details regarding adding assets from package dependencies, see + # https://flutter.dev/to/asset-from-package + + # To add custom fonts to your application, add a fonts section here, + # in this "flutter" section. Each entry in this list should have a + # "family" key with the font family name, and a "fonts" key with a + # list giving the asset and other descriptors for the font. For + # example: + # fonts: + # - family: Schyler + # fonts: + # - asset: fonts/Schyler-Regular.ttf + # - asset: fonts/Schyler-Italic.ttf + # style: italic + # - family: Trajan Pro + # fonts: + # - asset: fonts/TrajanPro.ttf + # - asset: fonts/TrajanPro_Bold.ttf + # weight: 700 + # + # For details regarding fonts from package dependencies, + # see https://flutter.dev/to/font-from-package diff --git a/stac_playground/test/dart_edit_preview_test.dart b/stac_playground/test/dart_edit_preview_test.dart new file mode 100644 index 00000000..131b4a1b --- /dev/null +++ b/stac_playground/test/dart_edit_preview_test.dart @@ -0,0 +1,119 @@ +import 'package:flutter_test/flutter_test.dart'; +import 'package:stac_playground/app/cubit/home_cubit.dart'; +import 'package:stac_playground/data/dsl_to_json.dart'; + +/// `parseEditorSource` is what the editor calls on every keystroke, so it +/// decides whether the live preview follows the Dart pane and what the user is +/// told when it can't. +void main() { + const dartScreen = ''' +import 'package:stac_core/stac_core.dart'; + +@StacScreen(screenName: 'demo') +StacWidget demo() { + return StacScaffold( + body: StacCenter(child: StacText(data: 'edited from Dart')), + ); +} +'''; + + test('editing Dart yields a widget tree for the preview', () { + final result = parseEditorSource(dartScreen, isDart: true); + + expect(result.message, isNull); + final json = result.json!; + expect(json['type'], 'scaffold'); + expect((json['body'] as Map)['type'], 'center'); + final text = (json['body'] as Map)['child'] as Map; + expect(text['type'], 'text'); + expect(text['data'], 'edited from Dart'); + }); + + test('single-expression helpers are inlined, not rejected', () { + const withHelper = ''' +import 'package:stac_core/stac_core.dart'; + +@StacScreen(screenName: 'demo') +StacWidget demo() { + return StacScaffold(body: _row(label: 'from helper')); +} + +StacWidget _row({required String label}) { + return StacRow(children: [StacText(data: label)]); +} +'''; + + final result = parseEditorSource(withHelper, isDart: true); + + expect(result.message, isNull); + final row = (result.json!['body'] as Map); + expect(row['type'], 'row'); + expect(((row['children'] as List).first as Map)['data'], 'from helper'); + }); + + test('the built-in screens preview too', () { + // hello_stac and form_screen carry their DSL inline on the cubit rather + // than in lib/dsl, and both lean on helpers (_socialRow, _fieldDecoration) + // — they're the first screens anyone opens, so they must stay previewable. + for (final entry in { + 'hello_stac': helloStacDartCode, + 'form_screen': formDartCode, + }.entries) { + final result = parseEditorSource(entry.value, isDart: true); + expect(result.message, isNull, reason: '${entry.key}: ${result.message}'); + expect(result.json?['type'], 'scaffold', reason: entry.key); + } + }); + + test('raw strings keep a literal dollar sign', () { + const rawDollar = r''' +import 'package:stac_core/stac_core.dart'; + +@StacScreen(screenName: 'demo') +StacWidget demo() { + return StacScaffold(body: StacText(data: r'costs $100')); +} +'''; + + final result = parseEditorSource(rawDollar, isDart: true); + + expect(result.message, isNull, reason: r'$ is literal in a raw string'); + expect((result.json!['body'] as Map)['data'], r'costs $100'); + }); + + test('Dart needing evaluation reports why the preview stopped', () { + const withVariable = ''' +import 'package:stac_core/stac_core.dart'; + +@StacScreen(screenName: 'demo') +StacWidget demo() { + return StacScaffold(body: someRuntimeWidget); +} +'''; + + final result = parseEditorSource(withVariable, isDart: true); + + expect(result.json, isNull, reason: 'preview must keep the last good tree'); + expect(result.message, contains('someRuntimeWidget')); + expect(result.message, contains('cannot be previewed')); + }); + + test('half-typed Dart holds the preview without nagging', () { + final result = parseEditorSource( + 'import 1;\n@StacScreen(screenName: 1)\nStacWidget d() { return Stac', + isDart: true, + ); + expect(result.json, isNull); + }); + + test('JSON still drives the preview, and bad JSON is silent', () { + final ok = + parseEditorSource('{"type": "text", "data": "hi"}', isDart: false); + expect(ok.json, {'type': 'text', 'data': 'hi'}); + expect(ok.message, isNull); + + final broken = parseEditorSource('{"type": "text",', isDart: false); + expect(broken.json, isNull); + expect(broken.message, isNull, reason: 'no banner for a mid-edit typo'); + }); +} diff --git a/stac_playground/test/dsl_to_json_test.dart b/stac_playground/test/dsl_to_json_test.dart new file mode 100644 index 00000000..948e122c --- /dev/null +++ b/stac_playground/test/dsl_to_json_test.dart @@ -0,0 +1,213 @@ +import 'dart:convert'; +import 'dart:io'; + +import 'package:flutter_test/flutter_test.dart'; +import 'package:stac_playground/data/dsl_to_json.dart'; + +/// Every DSL example should parse, and the JSON it produces should match the +/// example's JSON asset — that equivalence is the playground's whole pitch. +void main() { + final dslDir = Directory('lib/dsl'); + final files = dslDir + .listSync() + .whereType() + .where((f) => f.path.endsWith('.dart')) + .toList() + ..sort((a, b) => a.path.compareTo(b.path)); + + test('every DSL file is in the declarative subset', () { + final failures = []; + for (final file in files) { + final id = file.uri.pathSegments.last.replaceAll('.dart', ''); + try { + dslToJson(file.readAsStringSync()); + } on DslParseException catch (e) { + failures.add('$id: $e'); + } catch (e) { + failures.add('$id: unexpected ${e.runtimeType}: $e'); + } + } + // Every example must parse, including the ones built from private helper + // functions (navigation, table) — those are inlined by the parser. + expect( + failures, + isEmpty, + reason: 'These should parse but did not:\n${failures.join('\n')}', + ); + }); + + test('parsed DSL produces a renderable widget tree', () { + final problems = []; + for (final file in files) { + final id = file.uri.pathSegments.last.replaceAll('.dart', ''); + Map produced; + try { + produced = dslToJson(file.readAsStringSync()); + } catch (_) { + continue; // covered by the parse test above + } + if (produced['type'] is! String) { + problems.add('$id: root has no widget "type"'); + continue; + } + try { + // The preview hands this straight to Stac.fromJson, so it has to be + // encodable and every nested widget needs a type. + jsonEncode(produced); + } catch (e) { + problems.add('$id: not JSON-encodable: $e'); + } + final untyped = _untypedWidgets(produced, r'$'); + if (untyped.isNotEmpty) { + problems.add('$id: ${untyped.take(3).join(', ')}'); + } + } + expect(problems, isEmpty, reason: problems.join('\n')); + }); + + test('examples that round-trip exactly keep doing so', () { + // These DSL files reproduce their JSON asset byte-for-byte (structurally), + // so they're the tightest correctness gate on the parser. The rest differ + // only via documented divergences — no-op keys the DSL drops, `null` + // placeholders for empty `{}` actions, and equivalent encodings such as + // `StacEdgeInsets.all(12)` → `12`, which fromJson expands. The set may + // grow; it must not shrink. + const exact = { + 'align', + 'aspect_ratio', + 'auto_complete', + 'carousel_view', + 'center', + 'circular_progress_indicator', + 'clip_oval', + 'column', + 'conditional', + 'divider', + 'drawer', + 'dropdown_menu_view', + 'dynamic_view', + 'fitted_box', + 'floating_action_button', + 'fractionally_sized_box', + 'icon', + 'icon_button', + 'limited_box', + 'linear_progress_indicator', + 'list_tile', + 'list_view', + 'navigation_bar', + 'opacity', + 'outlined_button', + 'padding', + 'page_view', + 'placeholder', + 'row', + 'scaffold', + 'scroll_view', + 'selectable_text', + 'sized_box', + 'slider', + 'sliver_app_bar', + 'sliver_fill_remaining', + 'sliver_grid', + 'sliver_list', + 'sliver_opacity', + 'sliver_padding', + 'sliver_to_box_adapter', + 'sliver_visibility', + 'spacer', + 'switch', + 'tab_bar', + 'text_button', + 'tool_tip', + 'web_view', + 'wrap', + }; + final broken = []; + for (final id in exact) { + final dartFile = File('lib/dsl/$id.dart'); + final jsonFile = File('assets/json/${id}_example.json'); + if (!dartFile.existsSync() || !jsonFile.existsSync()) { + broken.add('$id: source missing'); + continue; + } + try { + final produced = dslToJson(dartFile.readAsStringSync()); + final expected = + jsonDecode(jsonFile.readAsStringSync()) as Map; + final diff = _diff(expected, produced, r'$'); + if (diff.isNotEmpty) broken.add('$id: ${diff.take(3).join('; ')}'); + } catch (e) { + broken.add('$id: $e'); + } + } + expect( + broken, + isEmpty, + reason: 'These no longer round-trip:\n${broken.join('\n')}', + ); + }); +} + +/// Walks the tree looking for nested maps that carry widget-ish keys but no +/// `type`, which would fail to render. +List _untypedWidgets(dynamic node, String path) { + final out = []; + if (node is Map) { + for (final entry in node.entries) { + final key = entry.key; + final value = entry.value; + // Only `child` is reliably a widget — `body`, for instance, is the HTTP + // payload on StacNetworkRequest. + if (key == 'child' && value is Map && value['type'] is! String) { + out.add('$path.$key has no type'); + } + out.addAll(_untypedWidgets(value, '$path.$key')); + } + } else if (node is List) { + for (var i = 0; i < node.length; i++) { + out.addAll(_untypedWidgets(node[i], '$path[$i]')); + } + } + return out; +} + +/// Structural diff. Keys the DSL intentionally drops (documented no-ops and +/// unmappable fields) still surface here so they're visible rather than hidden. +List _diff(dynamic expected, dynamic actual, String path) { + final out = []; + if (expected is Map && actual is Map) { + for (final key in expected.keys) { + if (!actual.containsKey(key)) { + out.add('$path.$key missing (expected ${_short(expected[key])})'); + continue; + } + out.addAll(_diff(expected[key], actual[key], '$path.$key')); + } + for (final key in actual.keys) { + if (!expected.containsKey(key)) { + out.add('$path.$key extra (${_short(actual[key])})'); + } + } + } else if (expected is List && actual is List) { + if (expected.length != actual.length) { + out.add('$path length ${expected.length} != ${actual.length}'); + } else { + for (var i = 0; i < expected.length; i++) { + out.addAll(_diff(expected[i], actual[i], '$path[$i]')); + } + } + } else if (expected is num && actual is num) { + if (expected.toDouble() != actual.toDouble()) { + out.add('$path: $expected != $actual'); + } + } else if (expected.toString() != actual.toString()) { + out.add('$path: ${_short(expected)} != ${_short(actual)}'); + } + return out; +} + +String _short(dynamic v) { + final s = jsonEncode(v); + return s.length > 60 ? '${s.substring(0, 57)}…' : s; +} diff --git a/stac_playground/web/favicon.png b/stac_playground/web/favicon.png new file mode 100644 index 00000000..54fe3043 Binary files /dev/null and b/stac_playground/web/favicon.png differ diff --git a/stac_playground/web/icons/Icon-192.png b/stac_playground/web/icons/Icon-192.png new file mode 100644 index 00000000..fe240028 Binary files /dev/null and b/stac_playground/web/icons/Icon-192.png differ diff --git a/stac_playground/web/icons/Icon-512.png b/stac_playground/web/icons/Icon-512.png new file mode 100644 index 00000000..8b789172 Binary files /dev/null and b/stac_playground/web/icons/Icon-512.png differ diff --git a/stac_playground/web/icons/Icon-maskable-192.png b/stac_playground/web/icons/Icon-maskable-192.png new file mode 100644 index 00000000..fe240028 Binary files /dev/null and b/stac_playground/web/icons/Icon-maskable-192.png differ diff --git a/stac_playground/web/icons/Icon-maskable-512.png b/stac_playground/web/icons/Icon-maskable-512.png new file mode 100644 index 00000000..8b789172 Binary files /dev/null and b/stac_playground/web/icons/Icon-maskable-512.png differ diff --git a/stac_playground/web/index.html b/stac_playground/web/index.html new file mode 100644 index 00000000..7d0f5158 --- /dev/null +++ b/stac_playground/web/index.html @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Stac Playground + + + + + + diff --git a/examples/stac_gallery/web/manifest.json b/stac_playground/web/manifest.json similarity index 85% rename from examples/stac_gallery/web/manifest.json rename to stac_playground/web/manifest.json index 089dc17e..6e3ef01e 100644 --- a/examples/stac_gallery/web/manifest.json +++ b/stac_playground/web/manifest.json @@ -1,11 +1,11 @@ { - "name": "stac_gallery", - "short_name": "stac_gallery", + "name": "Stac Playground", + "short_name": "Stac Playground", "start_url": ".", "display": "standalone", "background_color": "#0175C2", "theme_color": "#0175C2", - "description": "A Stac gallery app to showcase all the Stac widgets", + "description": "Stac Playground", "orientation": "portrait-primary", "prefer_related_applications": false, "icons": [ diff --git a/examples/stac_gallery/windows/.gitignore b/stac_playground/windows/.gitignore similarity index 100% rename from examples/stac_gallery/windows/.gitignore rename to stac_playground/windows/.gitignore diff --git a/examples/stac_gallery/windows/CMakeLists.txt b/stac_playground/windows/CMakeLists.txt similarity index 91% rename from examples/stac_gallery/windows/CMakeLists.txt rename to stac_playground/windows/CMakeLists.txt index c2c29b93..00580618 100644 --- a/examples/stac_gallery/windows/CMakeLists.txt +++ b/stac_playground/windows/CMakeLists.txt @@ -1,14 +1,14 @@ # Project-level configuration. cmake_minimum_required(VERSION 3.14) -project(stac_gallery LANGUAGES CXX) +project(stac_playground LANGUAGES CXX) # The name of the executable created for the application. Change this to change # the on-disk name of your application. -set(BINARY_NAME "stac_gallery") +set(BINARY_NAME "stac_playground") # Explicitly opt in to modern CMake behaviors to avoid warnings with recent # versions of CMake. -cmake_policy(SET CMP0063 NEW) +cmake_policy(VERSION 3.14...3.25) # Define build configuration option. get_property(IS_MULTICONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) @@ -52,6 +52,7 @@ add_subdirectory(${FLUTTER_MANAGED_DIR}) # Application build; see runner/CMakeLists.txt. add_subdirectory("runner") + # Generated plugin build rules, which manage building the plugins and adding # them to the application. include(flutter/generated_plugins.cmake) @@ -86,6 +87,12 @@ if(PLUGIN_BUNDLED_LIBRARIES) COMPONENT Runtime) endif() +# Copy the native assets provided by the build.dart from all packages. +set(NATIVE_ASSETS_DIR "${PROJECT_BUILD_DIR}native_assets/windows/") +install(DIRECTORY "${NATIVE_ASSETS_DIR}" + DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) + # Fully re-copy the assets directory on each build to avoid having stale files # from a previous install. set(FLUTTER_ASSET_DIR_NAME "flutter_assets") diff --git a/examples/stac_gallery/windows/flutter/CMakeLists.txt b/stac_playground/windows/flutter/CMakeLists.txt similarity index 94% rename from examples/stac_gallery/windows/flutter/CMakeLists.txt rename to stac_playground/windows/flutter/CMakeLists.txt index 930d2071..903f4899 100644 --- a/examples/stac_gallery/windows/flutter/CMakeLists.txt +++ b/stac_playground/windows/flutter/CMakeLists.txt @@ -10,6 +10,11 @@ include(${EPHEMERAL_DIR}/generated_config.cmake) # https://github.com/flutter/flutter/issues/57146. set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper") +# Set fallback configurations for older versions of the flutter tool. +if (NOT DEFINED FLUTTER_TARGET_PLATFORM) + set(FLUTTER_TARGET_PLATFORM "windows-x64") +endif() + # === Flutter Library === set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/flutter_windows.dll") @@ -92,7 +97,7 @@ add_custom_command( COMMAND ${CMAKE_COMMAND} -E env ${FLUTTER_TOOL_ENVIRONMENT} "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat" - windows-x64 $ + ${FLUTTER_TARGET_PLATFORM} $ VERBATIM ) add_custom_target(flutter_assemble DEPENDS diff --git a/examples/stac_gallery/windows/flutter/generated_plugin_registrant.cc b/stac_playground/windows/flutter/generated_plugin_registrant.cc similarity index 50% rename from examples/stac_gallery/windows/flutter/generated_plugin_registrant.cc rename to stac_playground/windows/flutter/generated_plugin_registrant.cc index 8b6d4680..4f788487 100644 --- a/examples/stac_gallery/windows/flutter/generated_plugin_registrant.cc +++ b/stac_playground/windows/flutter/generated_plugin_registrant.cc @@ -6,6 +6,9 @@ #include "generated_plugin_registrant.h" +#include void RegisterPlugins(flutter::PluginRegistry* registry) { + UrlLauncherWindowsRegisterWithRegistrar( + registry->GetRegistrarForPlugin("UrlLauncherWindows")); } diff --git a/examples/stac_gallery/windows/flutter/generated_plugin_registrant.h b/stac_playground/windows/flutter/generated_plugin_registrant.h similarity index 100% rename from examples/stac_gallery/windows/flutter/generated_plugin_registrant.h rename to stac_playground/windows/flutter/generated_plugin_registrant.h diff --git a/examples/stac_gallery/windows/flutter/generated_plugins.cmake b/stac_playground/windows/flutter/generated_plugins.cmake similarity index 96% rename from examples/stac_gallery/windows/flutter/generated_plugins.cmake rename to stac_playground/windows/flutter/generated_plugins.cmake index b93c4c30..88b22e5c 100644 --- a/examples/stac_gallery/windows/flutter/generated_plugins.cmake +++ b/stac_playground/windows/flutter/generated_plugins.cmake @@ -3,6 +3,7 @@ # list(APPEND FLUTTER_PLUGIN_LIST + url_launcher_windows ) list(APPEND FLUTTER_FFI_PLUGIN_LIST diff --git a/examples/stac_gallery/windows/runner/CMakeLists.txt b/stac_playground/windows/runner/CMakeLists.txt similarity index 100% rename from examples/stac_gallery/windows/runner/CMakeLists.txt rename to stac_playground/windows/runner/CMakeLists.txt diff --git a/examples/stac_gallery/windows/runner/Runner.rc b/stac_playground/windows/runner/Runner.rc similarity index 90% rename from examples/stac_gallery/windows/runner/Runner.rc rename to stac_playground/windows/runner/Runner.rc index f9b9549c..39a48e31 100644 --- a/examples/stac_gallery/windows/runner/Runner.rc +++ b/stac_playground/windows/runner/Runner.rc @@ -90,12 +90,12 @@ BEGIN BLOCK "040904e4" BEGIN VALUE "CompanyName", "com.stac" "\0" - VALUE "FileDescription", "stac_gallery" "\0" + VALUE "FileDescription", "stac_playground" "\0" VALUE "FileVersion", VERSION_AS_STRING "\0" - VALUE "InternalName", "stac_gallery" "\0" - VALUE "LegalCopyright", "Copyright (C) 2023 com.stac. All rights reserved." "\0" - VALUE "OriginalFilename", "stac_gallery.exe" "\0" - VALUE "ProductName", "stac_gallery" "\0" + VALUE "InternalName", "stac_playground" "\0" + VALUE "LegalCopyright", "Copyright (C) 2024 com.stac. All rights reserved." "\0" + VALUE "OriginalFilename", "stac_playground.exe" "\0" + VALUE "ProductName", "stac_playground" "\0" VALUE "ProductVersion", VERSION_AS_STRING "\0" END END diff --git a/examples/stac_gallery/windows/runner/flutter_window.cpp b/stac_playground/windows/runner/flutter_window.cpp similarity index 87% rename from examples/stac_gallery/windows/runner/flutter_window.cpp rename to stac_playground/windows/runner/flutter_window.cpp index b25e363e..955ee303 100644 --- a/examples/stac_gallery/windows/runner/flutter_window.cpp +++ b/stac_playground/windows/runner/flutter_window.cpp @@ -31,6 +31,11 @@ bool FlutterWindow::OnCreate() { this->Show(); }); + // Flutter can complete the first frame before the "show window" callback is + // registered. The following call ensures a frame is pending to ensure the + // window is shown. It is a no-op if the first frame hasn't completed yet. + flutter_controller_->ForceRedraw(); + return true; } diff --git a/examples/stac_gallery/windows/runner/flutter_window.h b/stac_playground/windows/runner/flutter_window.h similarity index 100% rename from examples/stac_gallery/windows/runner/flutter_window.h rename to stac_playground/windows/runner/flutter_window.h diff --git a/examples/stac_gallery/windows/runner/main.cpp b/stac_playground/windows/runner/main.cpp similarity index 95% rename from examples/stac_gallery/windows/runner/main.cpp rename to stac_playground/windows/runner/main.cpp index 430393aa..06548cf7 100644 --- a/examples/stac_gallery/windows/runner/main.cpp +++ b/stac_playground/windows/runner/main.cpp @@ -27,7 +27,7 @@ int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev, FlutterWindow window(project); Win32Window::Point origin(10, 10); Win32Window::Size size(1280, 720); - if (!window.Create(L"stac_gallery", origin, size)) { + if (!window.Create(L"stac_playground", origin, size)) { return EXIT_FAILURE; } window.SetQuitOnClose(true); diff --git a/examples/stac_gallery/windows/runner/resource.h b/stac_playground/windows/runner/resource.h similarity index 100% rename from examples/stac_gallery/windows/runner/resource.h rename to stac_playground/windows/runner/resource.h diff --git a/examples/stac_gallery/windows/runner/resources/app_icon.ico b/stac_playground/windows/runner/resources/app_icon.ico similarity index 100% rename from examples/stac_gallery/windows/runner/resources/app_icon.ico rename to stac_playground/windows/runner/resources/app_icon.ico diff --git a/examples/stac_gallery/windows/runner/runner.exe.manifest b/stac_playground/windows/runner/runner.exe.manifest similarity index 68% rename from examples/stac_gallery/windows/runner/runner.exe.manifest rename to stac_playground/windows/runner/runner.exe.manifest index a42ea768..153653e8 100644 --- a/examples/stac_gallery/windows/runner/runner.exe.manifest +++ b/stac_playground/windows/runner/runner.exe.manifest @@ -9,12 +9,6 @@ - - - - - - diff --git a/examples/stac_gallery/windows/runner/utils.cpp b/stac_playground/windows/runner/utils.cpp similarity index 85% rename from examples/stac_gallery/windows/runner/utils.cpp rename to stac_playground/windows/runner/utils.cpp index f5bf9fa0..3a0b4651 100644 --- a/examples/stac_gallery/windows/runner/utils.cpp +++ b/stac_playground/windows/runner/utils.cpp @@ -45,9 +45,11 @@ std::string Utf8FromUtf16(const wchar_t* utf16_string) { if (utf16_string == nullptr) { return std::string(); } - int target_length = ::WideCharToMultiByte( + unsigned int target_length = ::WideCharToMultiByte( CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, - -1, nullptr, 0, nullptr, nullptr); + -1, nullptr, 0, nullptr, nullptr) + -1; // remove the trailing null character + int input_length = (int)wcslen(utf16_string); std::string utf8_string; if (target_length == 0 || target_length > utf8_string.max_size()) { return utf8_string; @@ -55,8 +57,7 @@ std::string Utf8FromUtf16(const wchar_t* utf16_string) { utf8_string.resize(target_length); int converted_length = ::WideCharToMultiByte( CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, - -1, utf8_string.data(), - target_length, nullptr, nullptr); + input_length, utf8_string.data(), target_length, nullptr, nullptr); if (converted_length == 0) { return std::string(); } diff --git a/examples/stac_gallery/windows/runner/utils.h b/stac_playground/windows/runner/utils.h similarity index 100% rename from examples/stac_gallery/windows/runner/utils.h rename to stac_playground/windows/runner/utils.h diff --git a/examples/stac_gallery/windows/runner/win32_window.cpp b/stac_playground/windows/runner/win32_window.cpp similarity index 99% rename from examples/stac_gallery/windows/runner/win32_window.cpp rename to stac_playground/windows/runner/win32_window.cpp index 041a3855..60608d0f 100644 --- a/examples/stac_gallery/windows/runner/win32_window.cpp +++ b/stac_playground/windows/runner/win32_window.cpp @@ -60,7 +60,7 @@ class WindowClassRegistrar { public: ~WindowClassRegistrar() = default; - // Returns the singleton registar instance. + // Returns the singleton registrar instance. static WindowClassRegistrar* GetInstance() { if (!instance_) { instance_ = new WindowClassRegistrar(); diff --git a/examples/stac_gallery/windows/runner/win32_window.h b/stac_playground/windows/runner/win32_window.h similarity index 97% rename from examples/stac_gallery/windows/runner/win32_window.h rename to stac_playground/windows/runner/win32_window.h index c86632d8..e901dde6 100644 --- a/examples/stac_gallery/windows/runner/win32_window.h +++ b/stac_playground/windows/runner/win32_window.h @@ -77,7 +77,7 @@ class Win32Window { // OS callback called by message pump. Handles the WM_NCCREATE message which // is passed when the non-client area is being created and enables automatic // non-client DPI scaling so that the non-client area automatically - // responsponds to changes in DPI. All other messages are handled by + // responds to changes in DPI. All other messages are handled by // MessageHandler. static LRESULT CALLBACK WndProc(HWND const window, UINT const message,