From 1ec2fcf4bf8f3a29585ccc3ea7db5638f39bf2d0 Mon Sep 17 00:00:00 2001 From: Divyanshu Bhargava Date: Sun, 2 Nov 2025 03:15:23 +0530 Subject: [PATCH 1/5] refactor: Update movie app structure and dependencies - Updated Dart SDK version in pubspec.yaml to ^3.8.0. - Upgraded dependencies including stac and stac_core. - Removed unused JSON screen files and light theme configuration. - Introduced new constants for API endpoints and asset paths. - Added default Stac options for initialization. - Implemented onboarding, home, and detail screens using Stac widgets. - Refactored movie carousel widget to use Stac's JSON serialization. - Enhanced theme management with a dedicated dark theme implementation. --- .../assets/jsons/screens/detail_screen.json | 408 ---------------- .../assets/jsons/screens/home_screen.json | 453 ------------------ .../jsons/screens/onboarding_screen.json | 102 ---- .../assets/jsons/theme/light_theme.json | 0 examples/movie_app/lib/constants/app_api.dart | 64 +++ .../movie_app/lib/constants/app_assets.dart | 22 + .../lib/constants/app_constants.dart | 9 + .../movie_app/lib/constants/app_strings.dart | 49 ++ .../movie_app/lib/default_stac_options.dart | 26 + examples/movie_app/lib/main.dart | 85 +--- examples/movie_app/lib/themes/app_theme.dart | 126 +++++ .../movie_carousel/movie_carousel.dart | 55 ++- .../movie_carousel/movie_carousel.g.dart | 17 + .../movie_carousel/movie_carousel_parser.dart | 8 +- examples/movie_app/pubspec.lock | 260 +++++++++- examples/movie_app/pubspec.yaml | 10 +- examples/movie_app/stac/app_theme.dart | 126 +++++ examples/movie_app/stac/detail_screen.dart | 290 +++++++++++ examples/movie_app/stac/home_screen.dart | 246 ++++++++++ .../movie_app/stac/onboarding_screen.dart | 78 +++ 20 files changed, 1377 insertions(+), 1057 deletions(-) delete mode 100644 examples/movie_app/assets/jsons/screens/detail_screen.json delete mode 100644 examples/movie_app/assets/jsons/screens/home_screen.json delete mode 100644 examples/movie_app/assets/jsons/screens/onboarding_screen.json delete mode 100644 examples/movie_app/assets/jsons/theme/light_theme.json create mode 100644 examples/movie_app/lib/constants/app_api.dart create mode 100644 examples/movie_app/lib/constants/app_assets.dart create mode 100644 examples/movie_app/lib/constants/app_constants.dart create mode 100644 examples/movie_app/lib/constants/app_strings.dart create mode 100644 examples/movie_app/lib/default_stac_options.dart create mode 100644 examples/movie_app/lib/themes/app_theme.dart create mode 100644 examples/movie_app/lib/widgets/movie_carousel/movie_carousel.g.dart create mode 100644 examples/movie_app/stac/app_theme.dart create mode 100644 examples/movie_app/stac/detail_screen.dart create mode 100644 examples/movie_app/stac/home_screen.dart create mode 100644 examples/movie_app/stac/onboarding_screen.dart diff --git a/examples/movie_app/assets/jsons/screens/detail_screen.json b/examples/movie_app/assets/jsons/screens/detail_screen.json deleted file mode 100644 index 0dbd26cf7..000000000 --- a/examples/movie_app/assets/jsons/screens/detail_screen.json +++ /dev/null @@ -1,408 +0,0 @@ -{ - "type": "scaffold", - "extendBodyBehindAppBar": true, - "appBar": { - "type": "appBar", - "backgroundColor": "transparent", - "leading": { - "type": "iconButton", - "icon": { - "type": "icon", - "icon": "chevron_left", - "color": "onSurface" - }, - "style": { - "backgroundColor": "#50050608", - "fixedSize": { - "width": 36, - "height": 36 - } - }, - "onPressed": { - "actionType": "navigate", - "navigationStyle": "pop" - } - } - }, - "body": { - "type": "dynamicView", - "request": { - "url": "https://api.themoviedb.org/3/movie/{{movie_id}}?language=en-US", - "method": "get" - }, - "template": { - "type": "singleChildScrollView", - "child": { - "type": "column", - "children": [ - { - "type": "stack", - "children": [ - { - "type": "image", - "src": "https://media.themoviedb.org/t/p/w440_and_h660_face/{{poster_path}}", - "width": 1000, - "height": 480, - "fit": "cover" - }, - { - "type": "positioned", - "bottom": 0, - "left": 0, - "right": 0, - "child": { - "type": "container", - "height": 240, - "decoration": { - "gradient": { - "colors": [ - "#00050608", - "#050608" - ], - "begin": "topCenter", - "end": "bottomCenter", - "stops": [ - 0.0, - 1.0 - ] - } - } - } - } - ] - }, - { - "type": "padding", - "padding": { - "left": 16, - "right": 16 - }, - "child": { - "type": "column", - "crossAxisAlignment": "start", - "children": [ - { - "type": "row", - "mainAxisAlignment": "spaceBetween", - "children": [ - { - "type": "expanded", - "child": { - "type": "text", - "data": "{{title}}", - "style": "headlineMedium", - "overflow": "ellipsis" - } - }, - { - "type": "container", - "height": 24, - "decoration": { - "borderRadius": 4, - "color": "primary" - }, - "child": { - "type": "row", - "children": [ - { - "type": "sizedBox", - "width": 6 - }, - { - "type": "icon", - "icon": "star_rounded", - "color": "onPrimary", - "size": 14 - }, - { - "type": "sizedBox", - "width": 2 - }, - { - "type": "text", - "data": "{{vote_average}}", - "style": { - "color": "onPrimary", - "fontSize": 14 - } - }, - { - "type": "sizedBox", - "width": 6 - } - ] - } - } - ] - }, - { - "type": "divider" - }, - { - "type": "text", - "data": "{{release_date}} · {{runtime}} mins", - "style": "bodySmall", - "textAlign": "left" - }, - { - "type": "divider" - }, - { - "type": "row", - "children": [ - { - "type": "expanded", - "child": { - "type": "filledButton", - "child": { - "type": "row", - "mainAxisAlignment": "center", - "children": [ - { - "type": "icon", - "icon": "play_circle_filled", - "size": 24 - }, - { - "type": "sizedBox", - "width": 6 - }, - { - "type": "text", - "data": "Watch Trailer" - } - ] - }, - "onPressed": {} - } - }, - { - "type": "sizedBox", - "width": 16 - }, - { - "type": "outlinedButton", - "child": { - "type": "row", - "mainAxisAlignment": "center", - "children": [ - { - "type": "icon", - "icon": "favorite_outline", - "size": 24 - }, - { - "type": "sizedBox", - "width": 6 - }, - { - "type": "text", - "data": "Add to Watchlist" - } - ] - }, - "onPressed": {} - } - ] - }, - { - "type": "sizedBox", - "height": 24 - }, - { - "type": "column", - "crossAxisAlignment": "start", - "children": [ - { - "type": "text", - "data": "About", - "style": "bodyMedium" - }, - { - "type": "sizedBox", - "height": 4 - }, - { - "type": "container", - "width": 24, - "height": 2, - "color": "primary" - } - ] - }, - { - "type": "sizedBox", - "height": 20 - }, - { - "type": "text", - "data": "{{overview}}", - "style": "bodyMedium" - }, - { - "type": "sizedBox", - "height": 24 - }, - { - "type": "column", - "crossAxisAlignment": "start", - "children": [ - { - "type": "text", - "data": "Cast", - "style": { - "fontSize": 16, - "fontWeight": "w600", - "height": 1.3, - "color": "onSurfaceVariant" - } - }, - { - "type": "sizedBox", - "height": 10 - }, - { - "type": "sizedBox", - "height": 146, - "child": { - "type": "dynamicView", - "request": { - "url": "https://api.themoviedb.org/3/movie/{{movie_id}}/credits?language=en-US", - "method": "get" - }, - "targetPath": "cast", - "template": { - "type": "listView", - "scrollDirection": "horizontal", - "shrinkWrap": true, - "separator": { - "type": "sizedBox", - "width": 16 - }, - "itemTemplate": { - "type": "sizedBox", - "width": 80, - "child": { - "type": "column", - "crossAxisAlignment": "start", - "children": [ - { - "type": "clipRRect", - "borderRadius": 6, - "child": { - "type": "image", - "src": "https://media.themoviedb.org/t/p/w440_and_h660_face/{{profile_path}}", - "fit": "cover", - "width": 80, - "height": 96 - } - }, - { - "type": "sizedBox", - "height": 8 - }, - { - "type": "text", - "data": "{{name}}", - "style": "titleSmall", - "overflow": "ellipsis" - }, - { - "type": "text", - "data": "{{character}}", - "style": "bodySmall", - "overflow": "ellipsis" - } - ] - } - } - } - } - } - ] - }, - { - "type": "sizedBox", - "height": 24 - }, - { - "type": "column", - "crossAxisAlignment": "start", - "children": [ - { - "type": "text", - "data": "Similar Movies", - "style": { - "fontSize": 16, - "fontWeight": "w600", - "height": 1.3, - "color": "onSurfaceVariant" - } - }, - { - "type": "sizedBox", - "height": 10 - }, - { - "type": "sizedBox", - "height": 164, - "child": { - "type": "dynamicView", - "request": { - "url": "https://api.themoviedb.org/3/movie/{{movie_id}}/similar?language=en-US&page=1", - "method": "get" - }, - "targetPath": "results", - "resultTarget": "data", - "template": { - "type": "listView", - "scrollDirection": "horizontal", - "shrinkWrap": true, - "separator": { - "type": "sizedBox", - "width": 8 - }, - "itemTemplate": { - "type": "gestureDetector", - "onTap": { - "actionType": "setValue", - "values": [ - { - "key": "movie_id", - "value": "{{data.id}}" - } - ], - "action": { - "actionType": "navigate", - "assetPath": "assets/jsons/screens/detail_screen.json" - } - }, - "child": { - "type": "clipRRect", - "borderRadius": 6, - "child": { - "type": "image", - "imageType": "network", - "src": "https://media.themoviedb.org/t/p/w440_and_h660_face/{{data.poster_path}}", - "width": 108, - "height": 164 - } - } - } - } - } - } - ] - }, - { - "type": "sizedBox", - "height": 80 - } - ] - } - } - ] - } - } - } -} \ No newline at end of file diff --git a/examples/movie_app/assets/jsons/screens/home_screen.json b/examples/movie_app/assets/jsons/screens/home_screen.json deleted file mode 100644 index 1a6144fe0..000000000 --- a/examples/movie_app/assets/jsons/screens/home_screen.json +++ /dev/null @@ -1,453 +0,0 @@ -{ - "type": "defaultBottomNavigationController", - "length": 3, - "child": { - "type": "scaffold", - "extendBodyBehindAppBar": true, - "body": { - "type": "bottomNavigationView", - "children": [ - { - "type": "listView", - "padding": 0, - "children": [ - { - "type": "movieCarousel", - "request": { - "url": "https://api.themoviedb.org/3/trending/movie/day?language=en-US", - "method": "get" - } - }, - { - "type": "column", - "children": [ - { - "type": "padding", - "padding": { - "left": 16, - "right": 16, - "top": 24, - "bottom": 10 - }, - "child": { - "type": "row", - "mainAxisAlignment": "spaceBetween", - "children": [ - { - "type": "text", - "data": "Now Playing", - "style": "labelLarge" - } - ] - } - }, - { - "type": "sizedBox", - "height": 164, - "child": { - "type": "dynamicView", - "request": { - "url": "https://api.themoviedb.org/3/movie/now_playing?language=en-US&page=1", - "method": "get" - }, - "targetPath": "results", - "template": { - "type": "listView", - "scrollDirection": "horizontal", - "shrinkWrap": true, - "separator": { - "type": "sizedBox", - "width": 8 - }, - "padding": { - "left": 16 - }, - "itemTemplate": { - "type": "gestureDetector", - "onTap": { - "actionType": "setValue", - "values": [ - { - "key": "movie_id", - "value": "{{id}}" - } - ], - "action": { - "actionType": "navigate", - "assetPath": "assets/jsons/screens/detail_screen.json" - } - }, - "child": { - "type": "clipRRect", - "borderRadius": 6, - "child": { - "type": "image", - "imageType": "network", - "src": "https://media.themoviedb.org/t/p/w440_and_h660_face/{{poster_path}}", - "width": 108, - "height": 164 - } - } - } - } - } - } - ] - }, - { - "type": "column", - "children": [ - { - "type": "padding", - "padding": { - "left": 16, - "right": 16, - "top": 24, - "bottom": 10 - }, - "child": { - "type": "row", - "mainAxisAlignment": "spaceBetween", - "children": [ - { - "type": "text", - "data": "Popular Movies", - "style": "labelLarge" - } - ] - } - }, - { - "type": "sizedBox", - "height": 164, - "child": { - "type": "dynamicView", - "request": { - "url": "https://api.themoviedb.org/3/movie/popular?language=en-US&page=1", - "method": "get" - }, - "targetPath": "results", - "template": { - "type": "listView", - "scrollDirection": "horizontal", - "shrinkWrap": true, - "separator": { - "type": "sizedBox", - "width": 8 - }, - "padding": { - "left": 16 - }, - "itemTemplate": { - "type": "gestureDetector", - "onTap": { - "actionType": "setValue", - "values": [ - { - "key": "movie_id", - "value": "{{id}}" - } - ], - "action": { - "actionType": "navigate", - "assetPath": "assets/jsons/screens/detail_screen.json" - } - }, - "child": { - "type": "clipRRect", - "borderRadius": 6, - "child": { - "type": "image", - "imageType": "network", - "src": "https://media.themoviedb.org/t/p/w440_and_h660_face/{{poster_path}}", - "width": 108, - "height": 164 - } - } - } - } - } - } - ] - }, - { - "type": "column", - "children": [ - { - "type": "padding", - "padding": { - "left": 16, - "right": 16, - "top": 24, - "bottom": 10 - }, - "child": { - "type": "row", - "mainAxisAlignment": "spaceBetween", - "children": [ - { - "type": "text", - "data": "Trending Movies", - "style": "labelLarge" - } - ] - } - }, - { - "type": "sizedBox", - "height": 164, - "child": { - "type": "dynamicView", - "request": { - "url": "https://api.themoviedb.org/3/trending/movie/day?language=en-US", - "method": "get" - }, - "targetPath": "results", - "template": { - "type": "listView", - "scrollDirection": "horizontal", - "shrinkWrap": true, - "separator": { - "type": "sizedBox", - "width": 8 - }, - "padding": { - "left": 20 - }, - "itemTemplate": { - "type": "gestureDetector", - "onTap": { - "actionType": "setValue", - "values": [ - { - "key": "movie_id", - "value": "{{id}}" - } - ], - "action": { - "actionType": "navigate", - "assetPath": "assets/jsons/screens/detail_screen.json" - } - }, - "child": { - "type": "clipRRect", - "borderRadius": 6, - "child": { - "type": "image", - "imageType": "network", - "src": "https://media.themoviedb.org/t/p/w440_and_h660_face/{{poster_path}}", - "width": 108, - "height": 164 - } - } - } - } - } - } - ] - }, - { - "type": "column", - "children": [ - { - "type": "padding", - "padding": { - "left": 16, - "right": 16, - "top": 24, - "bottom": 10 - }, - "child": { - "type": "row", - "mainAxisAlignment": "spaceBetween", - "children": [ - { - "type": "text", - "data": "Top Rated", - "style": "labelLarge" - } - ] - } - }, - { - "type": "sizedBox", - "height": 164, - "child": { - "type": "dynamicView", - "request": { - "url": "https://api.themoviedb.org/3/movie/top_rated?language=en-US&page=1", - "method": "get" - }, - "targetPath": "results", - "template": { - "type": "listView", - "scrollDirection": "horizontal", - "shrinkWrap": true, - "separator": { - "type": "sizedBox", - "width": 8 - }, - "padding": { - "left": 16 - }, - "itemTemplate": { - "type": "gestureDetector", - "onTap": { - "actionType": "setValue", - "values": [ - { - "key": "movie_id", - "value": "{{id}}" - } - ], - "action": { - "actionType": "navigate", - "assetPath": "assets/jsons/screens/detail_screen.json" - } - }, - "child": { - "type": "clipRRect", - "borderRadius": 6, - "child": { - "type": "image", - "imageType": "network", - "src": "https://media.themoviedb.org/t/p/w440_and_h660_face/{{poster_path}}", - "width": 108, - "height": 164 - } - } - } - } - } - } - ] - }, - { - "type": "column", - "children": [ - { - "type": "padding", - "padding": { - "left": 16, - "right": 16, - "top": 24, - "bottom": 10 - }, - "child": { - "type": "row", - "mainAxisAlignment": "spaceBetween", - "children": [ - { - "type": "text", - "data": "Upcoming Movies", - "style": "labelLarge" - } - ] - } - }, - { - "type": "sizedBox", - "height": 164, - "child": { - "type": "dynamicView", - "request": { - "url": "https://api.themoviedb.org/3/movie/upcoming?language=en-US&page=1", - "method": "get" - }, - "targetPath": "results", - "template": { - "type": "listView", - "scrollDirection": "horizontal", - "shrinkWrap": true, - "separator": { - "type": "sizedBox", - "width": 8 - }, - "padding": { - "left": 16 - }, - "itemTemplate": { - "type": "gestureDetector", - "onTap": { - "actionType": "setValue", - "values": [ - { - "key": "movie_id", - "value": "{{id}}" - } - ], - "action": { - "actionType": "navigate", - "assetPath": "assets/jsons/screens/detail_screen.json" - } - }, - "child": { - "type": "clipRRect", - "borderRadius": 6, - "child": { - "type": "image", - "imageType": "network", - "src": "https://media.themoviedb.org/t/p/w440_and_h660_face/{{poster_path}}", - "width": 108, - "height": 164 - } - } - } - } - } - } - ] - }, - { - "type": "sizedBox", - "height": 80 - } - ] - }, - { - "type": "center", - "child": { - "type": "text", - "data": "Search" - } - }, - { - "type": "center", - "child": { - "type": "text", - "data": "Profile" - } - } - ] - }, - "bottomNavigationBar": { - "type": "bottomNavigationBar", - "items": [ - { - "type": "navigationBarItem", - "label": "Home", - "icon": { - "type": "icon", - "icon": "home_outlined" - } - }, - { - "type": "navigationBarItem", - "label": "Search", - "icon": { - "type": "icon", - "icon": "search_outlined" - } - }, - { - "type": "navigationBarItem", - "label": "Profile", - "icon": { - "type": "icon", - "icon": "person_outlined" - } - } - ] - } - } -} \ No newline at end of file diff --git a/examples/movie_app/assets/jsons/screens/onboarding_screen.json b/examples/movie_app/assets/jsons/screens/onboarding_screen.json deleted file mode 100644 index b956160e8..000000000 --- a/examples/movie_app/assets/jsons/screens/onboarding_screen.json +++ /dev/null @@ -1,102 +0,0 @@ -{ - "type": "scaffold", - "body": { - "type": "stack", - "children": [ - { - "type": "image", - "imageType": "asset", - "src": "assets/images/image.png", - "width": 10000, - "height": 10000, - "fit": "cover" - }, - { - "type": "positioned", - "left": 0, - "right": 0, - "bottom": 0, - "child": { - "type": "container", - "width": 1000, - "height": 500, - "decoration": { - "gradient": { - "gradientType": "linear", - "colors": [ - "#00050608", - "#050608", - "#050608" - ], - "begin": "topCenter", - "end": "bottomCenter", - "stops": [ - 0.0, - 0.8, - 1.0 - ] - } - }, - "child": { - "type": "padding", - "padding": { - "left": 16, - "right": 16, - "top": 48, - "bottom": 48 - }, - "child": { - "type": "column", - "mainAxisAlignment": "end", - "crossAxisAlignment": "start", - "children": [ - { - "type": "text", - "data": "Movie ", - "style": "displayMedium", - "children": [ - { - "text": "\nDatabase", - "style": { - "color": "primary" - } - } - ] - }, - { - "type": "sizedBox", - "height": 24 - }, - { - "type": "text", - "data": "Watch & enjoy a variety of award winning TV shows, movies, anime, and a lot more", - "style": "bodyMedium" - }, - { - "type": "sizedBox", - "height": 64 - }, - { - "type": "sizedBox", - "height": 48, - "width": 1000, - "child": { - "type": "filledButton", - "child": { - "type": "text", - "data": "Get Started" - }, - "onPressed": { - "actionType": "navigate", - "assetPath": "assets/jsons/screens/home_screen.json" - } - } - } - ] - } - } - } - } - ] - } -} \ No newline at end of file diff --git a/examples/movie_app/assets/jsons/theme/light_theme.json b/examples/movie_app/assets/jsons/theme/light_theme.json deleted file mode 100644 index e69de29bb..000000000 diff --git a/examples/movie_app/lib/constants/app_api.dart b/examples/movie_app/lib/constants/app_api.dart new file mode 100644 index 000000000..a491b44e8 --- /dev/null +++ b/examples/movie_app/lib/constants/app_api.dart @@ -0,0 +1,64 @@ +/// Application API endpoints and configuration +/// +/// This file contains all API-related constants including base URLs, +/// endpoints, and API configuration. +class AppApi { + AppApi._(); // Private constructor to prevent instantiation + + // ============================================================================ + // Base Configuration + // ============================================================================ + + static const String baseUrl = 'https://api.themoviedb.org/3'; + static const String imageBaseUrl = + 'https://media.themoviedb.org/t/p/w440_and_h660_face'; + static const String language = 'en-US'; + + // ============================================================================ + // Movie Endpoints + // ============================================================================ + + /// Trending movies endpoint for the current day + static String getTrendingMoviesUrl([int page = 1]) => + '$baseUrl/trending/movie/day?language=$language&page=$page'; + + /// Now playing movies endpoint + static String getNowPlayingMoviesUrl([int page = 1]) => + '$baseUrl/movie/now_playing?language=$language&page=$page'; + + /// Popular movies endpoint + static String getPopularMoviesUrl([int page = 1]) => + '$baseUrl/movie/popular?language=$language&page=$page'; + + /// Top rated movies endpoint + static String getTopRatedMoviesUrl([int page = 1]) => + '$baseUrl/movie/top_rated?language=$language&page=$page'; + + /// Upcoming movies endpoint + static String getUpcomingMoviesUrl([int page = 1]) => + '$baseUrl/movie/upcoming?language=$language&page=$page'; + + /// Movie details endpoint + static String getMovieDetailsUrl(int movieId) => + '$baseUrl/movie/$movieId?language=$language'; + + /// Movie credits endpoint + static String getMovieCreditsUrl(int movieId) => + '$baseUrl/movie/$movieId/credits?language=$language'; + + /// Similar movies endpoint + static String getSimilarMoviesUrl(int movieId, [int page = 1]) => + '$baseUrl/movie/$movieId/similar?language=$language&page=$page'; + + // ============================================================================ + // Image URLs + // ============================================================================ + + /// Get full poster image URL from poster path + static String getPosterImageUrl(String posterPath) => + '$imageBaseUrl/$posterPath'; + + /// Get full profile image URL from profile path + static String getProfileImageUrl(String profilePath) => + '$imageBaseUrl/$profilePath'; +} diff --git a/examples/movie_app/lib/constants/app_assets.dart b/examples/movie_app/lib/constants/app_assets.dart new file mode 100644 index 000000000..8d018e42d --- /dev/null +++ b/examples/movie_app/lib/constants/app_assets.dart @@ -0,0 +1,22 @@ +/// Application asset paths +/// +/// This file contains all asset paths used throughout the application. +class AppAssets { + AppAssets._(); // Private constructor to prevent instantiation + + // ============================================================================ + // Images + // ============================================================================ + + static const String onboardingImage = 'assets/images/image.png'; + + // ============================================================================ + // JSON Screens + // ============================================================================ + + static const String onboardingScreenJson = + 'assets/jsons/screens/onboarding_screen.json'; + static const String homeScreenJson = 'assets/jsons/screens/home_screen.json'; + static const String detailScreenJson = + 'assets/jsons/screens/detail_screen.json'; +} diff --git a/examples/movie_app/lib/constants/app_constants.dart b/examples/movie_app/lib/constants/app_constants.dart new file mode 100644 index 000000000..7b0c277f5 --- /dev/null +++ b/examples/movie_app/lib/constants/app_constants.dart @@ -0,0 +1,9 @@ +library; + +/// Applicationwide constants +/// +/// This file exports all constants used throughout the application +/// for convenient access via a single import. +export 'app_api.dart'; +export 'app_assets.dart'; +export 'app_strings.dart'; diff --git a/examples/movie_app/lib/constants/app_strings.dart b/examples/movie_app/lib/constants/app_strings.dart new file mode 100644 index 000000000..4f4f49336 --- /dev/null +++ b/examples/movie_app/lib/constants/app_strings.dart @@ -0,0 +1,49 @@ +/// Application-wide string constants +/// +/// This file contains all user-facing strings and text content used throughout +/// the application. Grouped by feature/screen for easy maintenance. +class AppStrings { + AppStrings._(); // Private constructor to prevent instantiation + + // ============================================================================ + // Onboarding Screen + // ============================================================================ + + static const String onboardingTitle = 'Movie '; + static const String onboardingTitleAccent = '\nDatabase'; + static const String onboardingDescription = + 'Watch & enjoy a variety of award winning TV shows, movies, anime, and a lot more'; + static const String onboardingGetStartedButton = 'Get Started'; + + // ============================================================================ + // Home Screen + // ============================================================================ + + static const String nowPlaying = 'Now Playing'; + static const String popularMovies = 'Popular Movies'; + static const String trendingMovies = 'Trending Movies'; + static const String topRated = 'Top Rated'; + static const String upcomingMovies = 'Upcoming Movies'; + + // Bottom Navigation + static const String bottomNavHome = 'Home'; + static const String bottomNavSearch = 'Search'; + static const String bottomNavProfile = 'Profile'; + + // ============================================================================ + // Detail Screen + // ============================================================================ + + static const String watchTrailer = 'Watch Trailer'; + static const String addToWatchlist = 'Add to Watchlist'; + static const String about = 'About'; + static const String cast = 'Cast'; + static const String similarMovies = 'Similar Movies'; + + // ============================================================================ + // Common + // ============================================================================ + + static const String search = 'Search'; + static const String profile = 'Profile'; +} diff --git a/examples/movie_app/lib/default_stac_options.dart b/examples/movie_app/lib/default_stac_options.dart new file mode 100644 index 000000000..3fe4aa7e9 --- /dev/null +++ b/examples/movie_app/lib/default_stac_options.dart @@ -0,0 +1,26 @@ + +// This file is automatically generated by stac init. + +import 'package:stac_core/core/stac_options.dart'; + +/// Default [StacOptions] for use with your stac project. +/// +/// Use this to initialize stac **before** calling [runApp]. +/// +/// Example: +/// ```dart +/// import 'package:flutter/material.dart'; +/// import 'package:stac/stac.dart'; +/// import 'default_stac_options.dart'; +/// +/// void main() { +/// Stac.initialize(options: defaultStacOptions); +/// +/// runApp(...); +/// } +/// ``` +StacOptions get defaultStacOptions => StacOptions( + name: 'movie_app', + description: '', + projectId: 'pha1PAyoVRqREK5M2k3E', +); diff --git a/examples/movie_app/lib/main.dart b/examples/movie_app/lib/main.dart index da663068e..3ec8688a5 100644 --- a/examples/movie_app/lib/main.dart +++ b/examples/movie_app/lib/main.dart @@ -1,5 +1,7 @@ import 'package:dio/dio.dart'; import 'package:flutter/material.dart'; +import 'package:movie_app/default_stac_options.dart'; +import 'package:movie_app/themes/app_theme.dart'; import 'package:movie_app/widgets/movie_carousel/movie_carousel_parser.dart'; import 'package:stac/stac.dart'; @@ -17,7 +19,11 @@ void main() async { ), ); - await Stac.initialize(dio: dio, parsers: [MovieCarouselParser()]); + await Stac.initialize( + options: defaultStacOptions, + dio: dio, + parsers: [MovieCarouselParser()], + ); runApp(const MyApp()); } @@ -29,83 +35,10 @@ class MyApp extends StatelessWidget { Widget build(BuildContext context) { return StacApp( title: 'Flutter Demo', - theme: StacTheme.fromJson(darkThemeJson), + theme: darkTheme, homeBuilder: (_) { - return Stac.fromAssets('assets/jsons/screens/onboarding_screen.json'); + return Stac(routeName: 'onboarding_screen'); }, ); } } - -final Map lightThemeJson = { - "brightness": "light", - "colorScheme": { - "brightness": "light", - "primary": "#14865F", - "onPrimary": "#FFFFFF", - "secondary": "#14865F", - "onSecondary": "#FFFFFF", - "background": "#FFFFFF", - "onBackground": "#010810", - "surface": "#FFFFFF", - "onSurface": "#010810", - "surfaceVariant": "#F6F7F8", - "onSurfaceVariant": "#65010810", - "error": "#FD1717", - "onError": "#FFFFFF", - "outline": "#080110810", - "onOutline": "#120110810", - }, -}; - -final Map darkThemeJson = { - "brightness": "dark", - "colorScheme": { - "brightness": "dark", - "primary": "#95E183", - "onPrimary": "#050608", - "secondary": "#95E183", - "onSecondary": "#FFFFFF", - "background": "#050608", - "onBackground": "#FFFFFF", - "surface": "#050608", - "onSurface": "#FFFFFF", - "surfaceVariant": "#101214", - "onSurfaceVariant": "#65FFFFFF", - "error": "#FF6565", - "onError": "#050608", - "outline": "#08FFFFFF", - "onOutline": "#12FFFFFF", - }, - "textTheme": { - "displayLarge": {"fontSize": 48, "fontWeight": "w700", "height": 1.1}, - "displayMedium": {"fontSize": 40, "fontWeight": "w700", "height": 1.1}, - "displaySmall": {"fontSize": 34, "fontWeight": "w700", "height": 1.1}, - "headlineLarge": {"fontSize": 30, "fontWeight": "w700", "height": 1.3}, - "headlineMedium": {"fontSize": 26, "fontWeight": "w700", "height": 1.3}, - "headlineSmall": {"fontSize": 23, "fontWeight": "w700", "height": 1.3}, - "titleLarge": {"fontSize": 20, "fontWeight": "w500", "height": 1.3}, - "titleMedium": {"fontSize": 18, "fontWeight": "w500", "height": 1.3}, - "titleSmall": {"fontSize": 16, "fontWeight": "w500", "height": 1.3}, - "labelLarge": {"fontSize": 16, "fontWeight": "w700", "height": 1.3}, - "labelMedium": {"fontSize": 14, "fontWeight": "w600", "height": 1.3}, - "labelSmall": {"fontSize": 12, "fontWeight": "w500", "height": 1.3}, - "bodyLarge": {"fontSize": 18, "fontWeight": "w400", "height": 1.5}, - "bodyMedium": {"fontSize": 16, "fontWeight": "w400", "height": 1.5}, - "bodySmall": {"fontSize": 14, "fontWeight": "w400", "height": 1.5}, - }, - "filledButtonTheme": { - "minimumSize": {"width": 120, "height": 40}, - "textStyle": {"fontSize": 16, "fontWeight": "w500", "height": 1.3}, - "padding": {"left": 10, "right": 10, "top": 8, "bottom": 8}, - "shape": {"type": "roundedRectangleBorder", "borderRadius": 8}, - }, - "outlinedButtonTheme": { - "minimumSize": {"width": 120, "height": 40}, - "textStyle": {"fontSize": 16, "fontWeight": "w500", "height": 1.3}, - "padding": {"left": 10, "right": 10, "top": 8, "bottom": 8}, - "side": {"color": "#95E183", "width": 1.0}, - "shape": {"type": "roundedRectangleBorder", "borderRadius": 8}, - }, - "dividerTheme": {"color": "#24FFFFFF", "thickness": 1}, -}; diff --git a/examples/movie_app/lib/themes/app_theme.dart b/examples/movie_app/lib/themes/app_theme.dart new file mode 100644 index 000000000..6dc341341 --- /dev/null +++ b/examples/movie_app/lib/themes/app_theme.dart @@ -0,0 +1,126 @@ +import 'package:flutter/material.dart'; +import 'package:stac/src/parsers/theme/stac_color_scheme/stac_color_scheme.dart'; +import 'package:stac/src/parsers/theme/stac_divider_theme_data/stac_divider_theme_data.dart'; +import 'package:stac/src/parsers/theme/stac_text_theme/stac_text_theme.dart'; +import 'package:stac/src/parsers/theme/stac_theme/stac_theme.dart'; +import 'package:stac_core/stac_core.dart' + hide StacTheme; // Hide StacTheme from stac_core to use the one from stac + +/// Dark theme for the Movie App. +StacTheme get darkTheme { + return StacTheme( + brightness: Brightness.dark, + colorScheme: StacColorScheme( + brightness: Brightness.dark, + primary: '#95E183', + onPrimary: '#050608', + secondary: '#95E183', + onSecondary: '#FFFFFF', + surface: '#050608', + onSurface: '#FFFFFF', + onSurfaceVariant: '#65FFFFFF', + error: '#FF6565', + onError: '#050608', + outline: '#08FFFFFF', + ), + textTheme: StacTextTheme( + displayLarge: StacCustomTextStyle( + fontSize: 48, + fontWeight: StacFontWeight.w700, + height: 1.1, + ), + displayMedium: StacCustomTextStyle( + fontSize: 40, + fontWeight: StacFontWeight.w700, + height: 1.1, + ), + displaySmall: StacCustomTextStyle( + fontSize: 34, + fontWeight: StacFontWeight.w700, + height: 1.1, + ), + headlineLarge: StacCustomTextStyle( + fontSize: 30, + fontWeight: StacFontWeight.w700, + height: 1.3, + ), + headlineMedium: StacCustomTextStyle( + fontSize: 26, + fontWeight: StacFontWeight.w700, + height: 1.3, + ), + headlineSmall: StacCustomTextStyle( + fontSize: 23, + fontWeight: StacFontWeight.w700, + height: 1.3, + ), + titleLarge: StacCustomTextStyle( + fontSize: 20, + fontWeight: StacFontWeight.w500, + height: 1.3, + ), + titleMedium: StacCustomTextStyle( + fontSize: 18, + fontWeight: StacFontWeight.w500, + height: 1.3, + ), + titleSmall: StacCustomTextStyle( + fontSize: 16, + fontWeight: StacFontWeight.w500, + height: 1.3, + ), + labelLarge: StacCustomTextStyle( + fontSize: 16, + fontWeight: StacFontWeight.w700, + height: 1.3, + ), + labelMedium: StacCustomTextStyle( + fontSize: 14, + fontWeight: StacFontWeight.w600, + height: 1.3, + ), + labelSmall: StacCustomTextStyle( + fontSize: 12, + fontWeight: StacFontWeight.w500, + height: 1.3, + ), + bodyLarge: StacCustomTextStyle( + fontSize: 18, + fontWeight: StacFontWeight.w400, + height: 1.5, + ), + bodyMedium: StacCustomTextStyle( + fontSize: 16, + fontWeight: StacFontWeight.w400, + height: 1.5, + ), + bodySmall: StacCustomTextStyle( + fontSize: 14, + fontWeight: StacFontWeight.w400, + height: 1.5, + ), + ), + filledButtonTheme: StacButtonStyle( + minimumSize: StacSize(120, 40), + textStyle: StacCustomTextStyle( + fontSize: 16, + fontWeight: StacFontWeight.w500, + height: 1.3, + ), + padding: StacEdgeInsets.only(left: 10, right: 10, top: 8, bottom: 8), + shape: StacRoundedRectangleBorder(borderRadius: StacBorderRadius.all(8)), + ), + outlinedButtonTheme: StacButtonStyle( + minimumSize: StacSize(120, 40), + textStyle: StacCustomTextStyle( + fontSize: 16, + fontWeight: StacFontWeight.w500, + height: 1.3, + ), + padding: StacEdgeInsets.only(left: 10, right: 10, top: 8, bottom: 8), + side: StacBorderSide(color: '#95E183', width: 1.0), + shape: StacRoundedRectangleBorder(borderRadius: StacBorderRadius.all(8)), + ), + dividerTheme: StacDividerThemeData(color: '#24FFFFFF', thickness: 1), + ); +} diff --git a/examples/movie_app/lib/widgets/movie_carousel/movie_carousel.dart b/examples/movie_app/lib/widgets/movie_carousel/movie_carousel.dart index a021bab4f..0271fc7d9 100644 --- a/examples/movie_app/lib/widgets/movie_carousel/movie_carousel.dart +++ b/examples/movie_app/lib/widgets/movie_carousel/movie_carousel.dart @@ -1,15 +1,54 @@ +import 'package:json_annotation/json_annotation.dart'; import 'package:stac_core/actions/network_request/stac_network_request.dart'; +import 'package:stac_core/core/stac_widget.dart'; -class MovieCarousel { - MovieCarousel({required this.request}); +part 'movie_carousel.g.dart'; +/// A Stac model representing a movie carousel widget. +/// +/// Displays a carousel of trending movies fetched from a network request. +/// +/// {@tool snippet} +/// Dart Example: +/// ```dart +/// StacMovieCarousel( +/// request: StacNetworkRequest( +/// url: 'https://api.themoviedb.org/3/trending/movie/day', +/// method: Method.get, +/// ), +/// ) +/// ``` +/// {@end-tool} +/// +/// {@tool snippet} +/// JSON Example: +/// ```json +/// { +/// "type": "movieCarousel", +/// "request": { +/// "actionType": "networkRequest", +/// "url": "https://api.themoviedb.org/3/trending/movie/day", +/// "method": "get" +/// } +/// } +/// ``` +/// {@end-tool} +@JsonSerializable() +class StacMovieCarousel extends StacWidget { + const StacMovieCarousel({required this.request}); + + /// The network request to fetch movie data for the carousel. final StacNetworkRequest request; - factory MovieCarousel.fromJson(Map json) { - return MovieCarousel(request: StacNetworkRequest.fromJson(json['request'])); - } + /// Widget type identifier. + @override + String get type => 'movieCarousel'; + + /// Creates a [StacMovieCarousel] from a JSON map. + factory StacMovieCarousel.fromJson(Map json) => + _$StacMovieCarouselFromJson(json); - Map toJson() { - return {'request': request.toJson()}; - } + /// Converts this [StacMovieCarousel] instance to a JSON map. + @override + Map toJson() => _$StacMovieCarouselToJson(this); } diff --git a/examples/movie_app/lib/widgets/movie_carousel/movie_carousel.g.dart b/examples/movie_app/lib/widgets/movie_carousel/movie_carousel.g.dart new file mode 100644 index 000000000..b35d52b8b --- /dev/null +++ b/examples/movie_app/lib/widgets/movie_carousel/movie_carousel.g.dart @@ -0,0 +1,17 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'movie_carousel.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +StacMovieCarousel _$StacMovieCarouselFromJson(Map json) => + StacMovieCarousel( + request: StacNetworkRequest.fromJson( + json['request'] as Map, + ), + ); + +Map _$StacMovieCarouselToJson(StacMovieCarousel instance) => + {'request': instance.request, 'type': instance.type}; diff --git a/examples/movie_app/lib/widgets/movie_carousel/movie_carousel_parser.dart b/examples/movie_app/lib/widgets/movie_carousel/movie_carousel_parser.dart index 65b240d4b..4f24a42f4 100644 --- a/examples/movie_app/lib/widgets/movie_carousel/movie_carousel_parser.dart +++ b/examples/movie_app/lib/widgets/movie_carousel/movie_carousel_parser.dart @@ -3,18 +3,18 @@ import 'package:movie_app/widgets/movie_carousel/movie_carousel.dart'; import 'package:smooth_page_indicator/smooth_page_indicator.dart'; import 'package:stac/stac.dart'; -class MovieCarouselParser extends StacParser { +class MovieCarouselParser extends StacParser { const MovieCarouselParser(); @override String get type => 'movieCarousel'; @override - MovieCarousel getModel(Map json) => - MovieCarousel.fromJson(json); + StacMovieCarousel getModel(Map json) => + StacMovieCarousel.fromJson(json); @override - Widget parse(BuildContext context, MovieCarousel model) { + Widget parse(BuildContext context, StacMovieCarousel model) { return FutureBuilder( future: StacNetworkService.request(context, model.request), builder: (context, snapshot) { diff --git a/examples/movie_app/pubspec.lock b/examples/movie_app/pubspec.lock index f8ebea79a..2c7934856 100644 --- a/examples/movie_app/pubspec.lock +++ b/examples/movie_app/pubspec.lock @@ -1,6 +1,22 @@ # Generated by pub # See https://dart.dev/tools/pub/glossary#lockfile packages: + _fe_analyzer_shared: + dependency: transitive + description: + name: _fe_analyzer_shared + sha256: c209688d9f5a5f26b2fb47a188131a6fb9e876ae9e47af3737c0b4f58a93470d + url: "https://pub.dev" + source: hosted + version: "91.0.0" + analyzer: + dependency: transitive + description: + name: analyzer + sha256: f51c8499b35f9b26820cfe914828a6a98a94efd5cc78b37bb7d03debae3a1d08 + url: "https://pub.dev" + source: hosted + version: "8.4.1" args: dependency: transitive description: @@ -25,6 +41,54 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.2" + build: + dependency: transitive + description: + name: build + sha256: dfb67ccc9a78c642193e0c2d94cb9e48c2c818b3178a86097d644acdcde6a8d9 + url: "https://pub.dev" + source: hosted + version: "4.0.2" + 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: "409002f1adeea601018715d613115cfaf0e31f512cb80ae4534c79867ae2363d" + url: "https://pub.dev" + source: hosted + version: "4.1.0" + build_runner: + dependency: "direct dev" + description: + name: build_runner + sha256: a9461b8e586bf018dd4afd2e13b49b08c6a844a4b226c8d1d10f3a723cdd78c3 + url: "https://pub.dev" + source: hosted + version: "2.10.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: a30f0a0e38671e89a492c44d005b5545b830a961575bbd8336d42869ff71066d + url: "https://pub.dev" + source: hosted + version: "8.12.0" cached_network_image: dependency: transitive description: @@ -57,6 +121,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.4.0" + checked_yaml: + dependency: transitive + description: + name: checked_yaml + sha256: "959525d3162f249993882720d52b7e0c833978df229be20702b33d48d91de70f" + url: "https://pub.dev" + source: hosted + version: "2.0.4" clock: dependency: transitive description: @@ -65,6 +137,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.1.2" + code_builder: + dependency: transitive + description: + name: code_builder + sha256: "11654819532ba94c34de52ff5feb52bd81cba1de00ef2ed622fd50295f9d4243" + url: "https://pub.dev" + source: hosted + version: "4.11.0" collection: dependency: transitive description: @@ -73,6 +153,14 @@ 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: @@ -89,6 +177,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.8" + dart_style: + dependency: transitive + description: + name: dart_style + sha256: c87dfe3d56f183ffe9106a18aebc6db431fc7c98c31a54b952a77f3d54a85697 + url: "https://pub.dev" + source: hosted + version: "3.1.2" dio: dependency: "direct main" description: @@ -179,6 +275,22 @@ packages: 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" http: dependency: transitive description: @@ -187,6 +299,14 @@ packages: 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" http_parser: dependency: transitive description: @@ -195,14 +315,30 @@ packages: url: "https://pub.dev" source: hosted version: "4.1.2" - json_annotation: + io: 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: "1ce844379ca14835a50d2f019a3099f419082cfdd231cd86a142af94dd5c6bb1" url: "https://pub.dev" source: hosted version: "4.9.0" + json_serializable: + dependency: "direct dev" + description: + name: json_serializable + sha256: "33a040668b31b320aafa4822b7b1e177e163fc3c1e835c6750319d4ab23aa6fe" + url: "https://pub.dev" + source: hosted + version: "6.11.1" leak_tracker: dependency: transitive description: @@ -243,6 +379,14 @@ packages: 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" matcher: dependency: transitive description: @@ -267,6 +411,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.16.0" + mime: + dependency: transitive + description: + name: mime + sha256: "41a20518f0cb1256669420fdba0cd90d21561e560ac240f26ef8322e45bb7ed6" + url: "https://pub.dev" + source: hosted + version: "2.0.0" octo_image: dependency: transitive description: @@ -275,6 +427,14 @@ 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: @@ -363,6 +523,30 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.8" + pool: + dependency: transitive + description: + name: pool + sha256: "978783255c543aa3586a1b3c21f6e9d720eb315376a915872c61ef8b5c20177d" + url: "https://pub.dev" + source: hosted + version: "1.5.2" + pub_semver: + dependency: transitive + description: + name: pub_semver + sha256: "5bfcf68ca79ef689f8990d1160781b4bad40a3bd5e5218ad4076ddb7f4081585" + 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" rxdart: dependency: transitive description: @@ -371,6 +555,22 @@ packages: url: "https://pub.dev" source: hosted version: "0.28.0" + 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 @@ -384,6 +584,22 @@ packages: url: "https://pub.dev" source: hosted version: "1.2.1" + source_gen: + dependency: transitive + description: + name: source_gen + sha256: "9098ab86015c4f1d8af6486b547b11100e73b193e1899015033cb3e14ad20243" + url: "https://pub.dev" + source: hosted + version: "4.0.2" + source_helper: + dependency: transitive + description: + name: source_helper + sha256: "6a3c6cc82073a8797f8c4dc4572146114a39652851c157db37e964d9c7038723" + url: "https://pub.dev" + source: hosted + version: "1.3.8" source_span: dependency: transitive description: @@ -484,6 +700,14 @@ 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: @@ -572,6 +796,14 @@ packages: url: "https://pub.dev" source: hosted version: "15.0.0" + watcher: + dependency: transitive + description: + name: watcher + sha256: "592ab6e2892f67760543fb712ff0177f4ec76c031f02f5b4ff8d3fc5eb9fb61a" + url: "https://pub.dev" + source: hosted + version: "1.1.4" web: dependency: transitive description: @@ -580,6 +812,22 @@ packages: 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" xdg_directories: dependency: transitive description: @@ -596,6 +844,14 @@ 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.8.0-0 <4.0.0" + dart: ">=3.9.0 <4.0.0" flutter: ">=3.27.0" diff --git a/examples/movie_app/pubspec.yaml b/examples/movie_app/pubspec.yaml index cb8a7eb67..4d65983b7 100644 --- a/examples/movie_app/pubspec.yaml +++ b/examples/movie_app/pubspec.yaml @@ -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.7.2 + sdk: ^3.8.0 # Dependencies specify other packages that your package needs in order to work. # To automatically upgrade your package dependencies to the latest versions @@ -34,10 +34,11 @@ dependencies: # The following adds the Cupertino Icons font to your application. # Use with the CupertinoIcons class for iOS style icons. cupertino_icons: ^1.0.8 - stac: ^1.0.0-dev.6 - stac_core: + stac: ^1.0.1 + stac_core: ^1.0.0 dio: ^5.8.0+1 smooth_page_indicator: ^1.2.1 + json_annotation: ^4.9.0 dev_dependencies: flutter_test: @@ -49,6 +50,8 @@ 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.0 + json_serializable: ^6.8.0 # For information on the generic Dart part of this file, see the # following page: https://dart.dev/tools/pub/pubspec @@ -61,7 +64,6 @@ flutter: # the material Icons class. uses-material-design: true assets: - - assets/jsons/screens/ - assets/images/ # To add assets to your application, add an assets section, like this: # assets: diff --git a/examples/movie_app/stac/app_theme.dart b/examples/movie_app/stac/app_theme.dart new file mode 100644 index 000000000..6dc341341 --- /dev/null +++ b/examples/movie_app/stac/app_theme.dart @@ -0,0 +1,126 @@ +import 'package:flutter/material.dart'; +import 'package:stac/src/parsers/theme/stac_color_scheme/stac_color_scheme.dart'; +import 'package:stac/src/parsers/theme/stac_divider_theme_data/stac_divider_theme_data.dart'; +import 'package:stac/src/parsers/theme/stac_text_theme/stac_text_theme.dart'; +import 'package:stac/src/parsers/theme/stac_theme/stac_theme.dart'; +import 'package:stac_core/stac_core.dart' + hide StacTheme; // Hide StacTheme from stac_core to use the one from stac + +/// Dark theme for the Movie App. +StacTheme get darkTheme { + return StacTheme( + brightness: Brightness.dark, + colorScheme: StacColorScheme( + brightness: Brightness.dark, + primary: '#95E183', + onPrimary: '#050608', + secondary: '#95E183', + onSecondary: '#FFFFFF', + surface: '#050608', + onSurface: '#FFFFFF', + onSurfaceVariant: '#65FFFFFF', + error: '#FF6565', + onError: '#050608', + outline: '#08FFFFFF', + ), + textTheme: StacTextTheme( + displayLarge: StacCustomTextStyle( + fontSize: 48, + fontWeight: StacFontWeight.w700, + height: 1.1, + ), + displayMedium: StacCustomTextStyle( + fontSize: 40, + fontWeight: StacFontWeight.w700, + height: 1.1, + ), + displaySmall: StacCustomTextStyle( + fontSize: 34, + fontWeight: StacFontWeight.w700, + height: 1.1, + ), + headlineLarge: StacCustomTextStyle( + fontSize: 30, + fontWeight: StacFontWeight.w700, + height: 1.3, + ), + headlineMedium: StacCustomTextStyle( + fontSize: 26, + fontWeight: StacFontWeight.w700, + height: 1.3, + ), + headlineSmall: StacCustomTextStyle( + fontSize: 23, + fontWeight: StacFontWeight.w700, + height: 1.3, + ), + titleLarge: StacCustomTextStyle( + fontSize: 20, + fontWeight: StacFontWeight.w500, + height: 1.3, + ), + titleMedium: StacCustomTextStyle( + fontSize: 18, + fontWeight: StacFontWeight.w500, + height: 1.3, + ), + titleSmall: StacCustomTextStyle( + fontSize: 16, + fontWeight: StacFontWeight.w500, + height: 1.3, + ), + labelLarge: StacCustomTextStyle( + fontSize: 16, + fontWeight: StacFontWeight.w700, + height: 1.3, + ), + labelMedium: StacCustomTextStyle( + fontSize: 14, + fontWeight: StacFontWeight.w600, + height: 1.3, + ), + labelSmall: StacCustomTextStyle( + fontSize: 12, + fontWeight: StacFontWeight.w500, + height: 1.3, + ), + bodyLarge: StacCustomTextStyle( + fontSize: 18, + fontWeight: StacFontWeight.w400, + height: 1.5, + ), + bodyMedium: StacCustomTextStyle( + fontSize: 16, + fontWeight: StacFontWeight.w400, + height: 1.5, + ), + bodySmall: StacCustomTextStyle( + fontSize: 14, + fontWeight: StacFontWeight.w400, + height: 1.5, + ), + ), + filledButtonTheme: StacButtonStyle( + minimumSize: StacSize(120, 40), + textStyle: StacCustomTextStyle( + fontSize: 16, + fontWeight: StacFontWeight.w500, + height: 1.3, + ), + padding: StacEdgeInsets.only(left: 10, right: 10, top: 8, bottom: 8), + shape: StacRoundedRectangleBorder(borderRadius: StacBorderRadius.all(8)), + ), + outlinedButtonTheme: StacButtonStyle( + minimumSize: StacSize(120, 40), + textStyle: StacCustomTextStyle( + fontSize: 16, + fontWeight: StacFontWeight.w500, + height: 1.3, + ), + padding: StacEdgeInsets.only(left: 10, right: 10, top: 8, bottom: 8), + side: StacBorderSide(color: '#95E183', width: 1.0), + shape: StacRoundedRectangleBorder(borderRadius: StacBorderRadius.all(8)), + ), + dividerTheme: StacDividerThemeData(color: '#24FFFFFF', thickness: 1), + ); +} diff --git a/examples/movie_app/stac/detail_screen.dart b/examples/movie_app/stac/detail_screen.dart new file mode 100644 index 000000000..91ecea146 --- /dev/null +++ b/examples/movie_app/stac/detail_screen.dart @@ -0,0 +1,290 @@ +import 'package:movie_app/constants/app_constants.dart'; +import 'package:stac_core/stac_core.dart'; + +@StacScreen(screenName: 'detail_screen') +StacWidget detailScreen() { + return StacScaffold( + extendBodyBehindAppBar: true, + appBar: StacAppBar( + backgroundColor: 'transparent', + leading: StacIconButton( + icon: StacIcon(icon: 'chevron_left', color: 'onSurface'), + style: StacButtonStyle( + backgroundColor: '#50050608', + fixedSize: StacSize(36, 36), + ), + onPressed: StacAction( + jsonData: {'actionType': 'navigate', 'navigationStyle': 'pop'}, + ), + ), + ), + body: StacDynamicView( + request: StacNetworkRequest( + url: '${AppApi.baseUrl}/movie/{{movie_id}}?language=${AppApi.language}', + method: Method.get, + ), + template: StacSingleChildScrollView( + child: StacColumn( + children: [ + StacStack( + children: [ + StacImage( + src: '${AppApi.imageBaseUrl}/{{poster_path}}', + width: 1000, + height: 480, + fit: StacBoxFit.cover, + ), + StacPositioned( + bottom: 0, + left: 0, + right: 0, + child: StacContainer( + height: 240, + decoration: StacBoxDecoration( + gradient: StacGradient.linear( + colors: ['#00050608', '#050608'], + begin: StacAlignment.topCenter, + end: StacAlignment.bottomCenter, + stops: [0.0, 1.0], + ), + ), + ), + ), + ], + ), + StacPadding( + padding: StacEdgeInsets.only(left: 16, right: 16), + child: StacColumn( + crossAxisAlignment: StacCrossAxisAlignment.start, + children: [ + StacRow( + mainAxisAlignment: StacMainAxisAlignment.spaceBetween, + children: [ + StacExpanded( + child: StacText( + data: '{{title}}', + style: StacTheme.textTheme.headlineMedium, + overflow: StacTextOverflow.ellipsis, + ), + ), + StacContainer( + height: 24, + decoration: StacBoxDecoration( + borderRadius: StacBorderRadius.all(4), + color: 'primary', + ), + child: StacRow( + children: [ + StacSizedBox(width: 6), + StacIcon( + icon: 'star_rounded', + color: 'onPrimary', + size: 14, + ), + StacSizedBox(width: 2), + StacText( + data: '{{vote_average}}', + style: StacCustomTextStyle( + color: 'onPrimary', + fontSize: 14, + ), + ), + StacSizedBox(width: 6), + ], + ), + ), + ], + ), + StacDivider(), + StacText( + data: '{{release_date}} · {{runtime}} mins', + style: StacTheme.textTheme.bodySmall, + textAlign: StacTextAlign.left, + ), + StacDivider(), + StacRow( + children: [ + StacExpanded( + child: StacFilledButton( + child: StacRow( + mainAxisAlignment: StacMainAxisAlignment.center, + children: [ + StacIcon(icon: 'play_circle_filled', size: 24), + StacSizedBox(width: 6), + StacText(data: AppStrings.watchTrailer), + ], + ), + onPressed: null, + ), + ), + StacSizedBox(width: 16), + StacOutlinedButton( + child: StacRow( + mainAxisAlignment: StacMainAxisAlignment.center, + children: [ + StacIcon(icon: 'favorite_outline', size: 24), + StacSizedBox(width: 6), + StacText(data: AppStrings.addToWatchlist), + ], + ), + onPressed: null, + ), + ], + ), + StacSizedBox(height: 24), + StacColumn( + crossAxisAlignment: StacCrossAxisAlignment.start, + children: [ + StacText( + data: AppStrings.about, + style: StacTheme.textTheme.bodyMedium, + ), + StacSizedBox(height: 4), + StacContainer(width: 24, height: 2, color: 'primary'), + ], + ), + StacSizedBox(height: 20), + StacText( + data: '{{overview}}', + style: StacTheme.textTheme.bodyMedium, + ), + StacSizedBox(height: 24), + StacColumn( + crossAxisAlignment: StacCrossAxisAlignment.start, + children: [ + StacText( + data: AppStrings.cast, + style: StacCustomTextStyle( + fontSize: 16, + fontWeight: StacFontWeight.w600, + height: 1.3, + color: 'onSurfaceVariant', + ), + ), + StacSizedBox(height: 10), + StacSizedBox( + height: 146, + child: StacDynamicView( + request: StacNetworkRequest( + url: + '${AppApi.baseUrl}/movie/{{movie_id}}/credits?language=${AppApi.language}', + method: Method.get, + ), + targetPath: 'cast', + template: _buildCastListViewTemplate(), + ), + ), + ], + ), + StacSizedBox(height: 24), + StacColumn( + crossAxisAlignment: StacCrossAxisAlignment.start, + children: [ + StacText( + data: AppStrings.similarMovies, + style: StacCustomTextStyle( + fontSize: 16, + fontWeight: StacFontWeight.w600, + height: 1.3, + color: 'onSurfaceVariant', + ), + ), + StacSizedBox(height: 10), + StacSizedBox( + height: 164, + child: StacDynamicView( + request: StacNetworkRequest( + url: + '${AppApi.baseUrl}/movie/{{movie_id}}/similar?language=${AppApi.language}&page=1', + method: Method.get, + ), + targetPath: 'results', + resultTarget: 'data', + template: _buildSimilarMoviesListViewTemplate(), + ), + ), + ], + ), + StacSizedBox(height: 80), + ], + ), + ), + ], + ), + ), + ), + ); +} + +/// Helper function to build a ListView template with itemTemplate for cast list. +StacWidget _buildCastListViewTemplate() { + final templateJson = { + 'type': 'listView', + 'scrollDirection': 'horizontal', + 'shrinkWrap': true, + 'separator': StacSizedBox(width: 16).toJson(), + 'itemTemplate': StacSizedBox( + width: 80, + child: StacColumn( + crossAxisAlignment: StacCrossAxisAlignment.start, + children: [ + StacClipRRect( + borderRadius: StacBorderRadius.all(6), + child: StacImage( + src: '${AppApi.imageBaseUrl}/{{profile_path}}', + fit: StacBoxFit.cover, + width: 80, + height: 96, + ), + ), + StacSizedBox(height: 8), + StacText( + data: '{{name}}', + style: StacTheme.textTheme.titleSmall, + overflow: StacTextOverflow.ellipsis, + ), + StacText( + data: '{{character}}', + style: StacTheme.textTheme.bodySmall, + overflow: StacTextOverflow.ellipsis, + ), + ], + ), + ).toJson(), + }; + + return StacWidget(jsonData: templateJson); +} + +/// Helper function to build a ListView template with itemTemplate for similar movies. +StacWidget _buildSimilarMoviesListViewTemplate() { + final templateJson = { + 'type': 'listView', + 'scrollDirection': 'horizontal', + 'shrinkWrap': true, + 'separator': StacSizedBox(width: 8).toJson(), + 'itemTemplate': StacGestureDetector( + onTap: StacAction.fromJson({ + 'actionType': 'setValue', + 'values': [ + {'key': 'movie_id', 'value': '{{data.id}}'}, + ], + 'action': { + 'actionType': 'navigate', + 'assetPath': AppAssets.detailScreenJson, + }, + }), + child: StacClipRRect( + borderRadius: StacBorderRadius.all(6), + child: StacImage( + imageType: StacImageType.network, + src: '${AppApi.imageBaseUrl}/{{data.poster_path}}', + width: 108, + height: 164, + ), + ), + ).toJson(), + }; + + return StacWidget(jsonData: templateJson); +} diff --git a/examples/movie_app/stac/home_screen.dart b/examples/movie_app/stac/home_screen.dart new file mode 100644 index 000000000..1227f4ce3 --- /dev/null +++ b/examples/movie_app/stac/home_screen.dart @@ -0,0 +1,246 @@ +import 'package:movie_app/constants/app_constants.dart'; +import 'package:movie_app/widgets/movie_carousel/movie_carousel.dart'; +import 'package:stac_core/stac_core.dart'; + +@StacScreen(screenName: 'home_screen') +StacWidget homeScreen() { + return StacDefaultBottomNavigationController( + length: 3, + child: StacScaffold( + extendBodyBehindAppBar: true, + body: StacBottomNavigationView( + children: [ + StacListView( + padding: StacEdgeInsets.all(0), + children: [ + StacMovieCarousel( + request: StacNetworkRequest( + url: AppApi.getTrendingMoviesUrl(), + method: Method.get, + ), + ), + StacColumn( + children: [ + StacPadding( + padding: StacEdgeInsets.only( + left: 16, + right: 16, + top: 24, + bottom: 10, + ), + child: StacRow( + mainAxisAlignment: StacMainAxisAlignment.spaceBetween, + children: [ + StacText( + data: AppStrings.nowPlaying, + style: StacTheme.textTheme.labelLarge, + ), + ], + ), + ), + StacSizedBox( + height: 164, + child: StacDynamicView( + request: StacNetworkRequest( + url: AppApi.getNowPlayingMoviesUrl(), + method: Method.get, + ), + targetPath: 'results', + template: _buildMovieListViewTemplate(), + ), + ), + ], + ), + StacColumn( + children: [ + StacPadding( + padding: StacEdgeInsets.only( + left: 16, + right: 16, + top: 24, + bottom: 10, + ), + child: StacRow( + mainAxisAlignment: StacMainAxisAlignment.spaceBetween, + children: [ + StacText( + data: AppStrings.popularMovies, + style: StacTheme.textTheme.labelLarge, + ), + ], + ), + ), + StacSizedBox( + height: 164, + child: StacDynamicView( + request: StacNetworkRequest( + url: AppApi.getPopularMoviesUrl(), + method: Method.get, + ), + targetPath: 'results', + template: _buildMovieListViewTemplate(), + ), + ), + ], + ), + StacColumn( + children: [ + StacPadding( + padding: StacEdgeInsets.only( + left: 16, + right: 16, + top: 24, + bottom: 10, + ), + child: StacRow( + mainAxisAlignment: StacMainAxisAlignment.spaceBetween, + children: [ + StacText( + data: AppStrings.trendingMovies, + style: StacTheme.textTheme.labelLarge, + ), + ], + ), + ), + StacSizedBox( + height: 164, + child: StacDynamicView( + request: StacNetworkRequest( + url: AppApi.getTrendingMoviesUrl(), + method: Method.get, + ), + targetPath: 'results', + template: _buildMovieListViewTemplate(), + ), + ), + ], + ), + StacColumn( + children: [ + StacPadding( + padding: StacEdgeInsets.only( + left: 16, + right: 16, + top: 24, + bottom: 10, + ), + child: StacRow( + mainAxisAlignment: StacMainAxisAlignment.spaceBetween, + children: [ + StacText( + data: AppStrings.topRated, + style: StacTheme.textTheme.labelLarge, + ), + ], + ), + ), + StacSizedBox( + height: 164, + child: StacDynamicView( + request: StacNetworkRequest( + url: AppApi.getTopRatedMoviesUrl(), + method: Method.get, + ), + targetPath: 'results', + template: _buildMovieListViewTemplate(), + ), + ), + ], + ), + StacColumn( + children: [ + StacPadding( + padding: StacEdgeInsets.only( + left: 16, + right: 16, + top: 24, + bottom: 10, + ), + child: StacRow( + mainAxisAlignment: StacMainAxisAlignment.spaceBetween, + children: [ + StacText( + data: AppStrings.upcomingMovies, + style: StacTheme.textTheme.labelLarge, + ), + ], + ), + ), + StacSizedBox( + height: 164, + child: StacDynamicView( + request: StacNetworkRequest( + url: AppApi.getUpcomingMoviesUrl(), + method: Method.get, + ), + targetPath: 'results', + template: _buildMovieListViewTemplate(), + ), + ), + ], + ), + StacSizedBox(height: 80), + ], + ), + StacCenter(child: StacText(data: AppStrings.search)), + StacCenter(child: StacText(data: AppStrings.profile)), + ], + ), + bottomNavigationBar: StacBottomNavigationBar( + items: [ + StacBottomNavigationBarItem( + label: AppStrings.bottomNavHome, + icon: StacIcon(icon: 'home_outlined'), + ), + StacBottomNavigationBarItem( + label: AppStrings.bottomNavSearch, + icon: StacIcon(icon: 'search_outlined'), + ), + StacBottomNavigationBarItem( + label: AppStrings.bottomNavProfile, + icon: StacIcon(icon: 'person_outlined'), + ), + ], + ), + ), + ); +} + +/// Helper function to build a ListView template with itemTemplate for movie lists. +/// Note: itemTemplate is a parser-specific feature handled by the dynamicView parser. +/// We construct the template as JSON to include itemTemplate. +StacWidget _buildMovieListViewTemplate() { + // Create template JSON with itemTemplate (parser-specific feature) + final templateJson = { + 'type': 'listView', + 'scrollDirection': 'horizontal', + 'shrinkWrap': true, + 'separator': StacSizedBox(width: 8).toJson(), + 'padding': StacEdgeInsets.only(left: 16).toJson(), + 'itemTemplate': + StacGestureDetector( + onTap: StacAction.fromJson({ + 'actionType': 'setValue', + 'values': [ + {'key': 'movie_id', 'value': '{{id}}'}, + ], + 'action': { + 'actionType': 'navigate', + 'assetPath': AppAssets.detailScreenJson, + }, + }), + child: StacClipRRect( + borderRadius: StacBorderRadius.all(6), + child: StacImage( + imageType: StacImageType.network, + src: '${AppApi.imageBaseUrl}/{{poster_path}}', + width: 108, + height: 164, + ), + ), + ).toJson(), + }; + + // Create a StacWidget with the JSON data + return StacWidget(jsonData: templateJson); +} diff --git a/examples/movie_app/stac/onboarding_screen.dart b/examples/movie_app/stac/onboarding_screen.dart new file mode 100644 index 000000000..4f34c7472 --- /dev/null +++ b/examples/movie_app/stac/onboarding_screen.dart @@ -0,0 +1,78 @@ +import 'package:movie_app/constants/app_constants.dart'; +import 'package:stac_core/stac_core.dart'; + +@StacScreen(screenName: 'onboarding_screen') +StacWidget onboardingScreen() { + return StacScaffold( + body: StacStack( + children: [ + StacImage( + imageType: StacImageType.asset, + src: AppAssets.onboardingImage, + width: 10000, + height: 10000, + fit: StacBoxFit.cover, + ), + StacPositioned( + left: 0, + right: 0, + bottom: 0, + child: StacContainer( + width: 1000, + height: 500, + decoration: StacBoxDecoration( + gradient: StacGradient.linear( + colors: ['#00050608', '#050608', '#050608'], + begin: StacAlignment.topCenter, + end: StacAlignment.bottomCenter, + stops: [0.0, 0.8, 1.0], + ), + ), + child: StacPadding( + padding: StacEdgeInsets.only( + left: 16, + right: 16, + top: 48, + bottom: 48, + ), + child: StacColumn( + mainAxisAlignment: StacMainAxisAlignment.end, + crossAxisAlignment: StacCrossAxisAlignment.start, + children: [ + StacText( + data: AppStrings.onboardingTitle, + style: StacTheme.textTheme.displayMedium, + children: [ + StacTextSpan( + text: AppStrings.onboardingTitleAccent, + style: StacCustomTextStyle(color: 'primary'), + ), + ], + ), + StacSizedBox(height: 24), + StacText( + data: AppStrings.onboardingDescription, + style: StacTheme.textTheme.bodyMedium, + ), + StacSizedBox(height: 64), + StacSizedBox( + height: 48, + width: 1000, + child: StacFilledButton( + child: StacText( + data: AppStrings.onboardingGetStartedButton, + ), + onPressed: StacNavigateAction( + assetPath: AppAssets.homeScreenJson, + ), + ), + ), + ], + ), + ), + ), + ), + ], + ), + ); +} From 149ed22b6069c89571512ad327fd34820fd304c3 Mon Sep 17 00:00:00 2001 From: Divyanshu Bhargava Date: Sat, 8 Nov 2025 00:10:34 +0530 Subject: [PATCH 2/5] refactor: Update movie app theme management and remove unused files - Removed the deprecated app_theme.dart file and updated references to StacThemeData in various screens. - Adjusted theme usage in detail, home, and onboarding screens to utilize StacThemeData for consistency. - Cleaned up pubspec.lock by removing unused freezed_annotation dependency. --- examples/movie_app/lib/themes/app_theme.dart | 12 +- examples/movie_app/pubspec.lock | 8 -- examples/movie_app/stac/app_theme.dart | 126 ------------------ examples/movie_app/stac/detail_screen.dart | 106 +++++++-------- examples/movie_app/stac/home_screen.dart | 50 ++++--- .../movie_app/stac/onboarding_screen.dart | 8 +- 6 files changed, 83 insertions(+), 227 deletions(-) delete mode 100644 examples/movie_app/stac/app_theme.dart diff --git a/examples/movie_app/lib/themes/app_theme.dart b/examples/movie_app/lib/themes/app_theme.dart index 6dc341341..84e694bca 100644 --- a/examples/movie_app/lib/themes/app_theme.dart +++ b/examples/movie_app/lib/themes/app_theme.dart @@ -1,17 +1,11 @@ -import 'package:flutter/material.dart'; -import 'package:stac/src/parsers/theme/stac_color_scheme/stac_color_scheme.dart'; -import 'package:stac/src/parsers/theme/stac_divider_theme_data/stac_divider_theme_data.dart'; -import 'package:stac/src/parsers/theme/stac_text_theme/stac_text_theme.dart'; -import 'package:stac/src/parsers/theme/stac_theme/stac_theme.dart'; -import 'package:stac_core/stac_core.dart' - hide StacTheme; // Hide StacTheme from stac_core to use the one from stac +import 'package:stac_core/stac_core.dart'; // Hide StacTheme from stac_core to use the one from stac /// Dark theme for the Movie App. StacTheme get darkTheme { return StacTheme( - brightness: Brightness.dark, + brightness: StacBrightness.dark, colorScheme: StacColorScheme( - brightness: Brightness.dark, + brightness: StacBrightness.dark, primary: '#95E183', onPrimary: '#050608', secondary: '#95E183', diff --git a/examples/movie_app/pubspec.lock b/examples/movie_app/pubspec.lock index 2c7934856..20ddc4db6 100644 --- a/examples/movie_app/pubspec.lock +++ b/examples/movie_app/pubspec.lock @@ -267,14 +267,6 @@ packages: description: flutter source: sdk version: "0.0.0" - freezed_annotation: - dependency: transitive - description: - name: freezed_annotation - sha256: "7294967ff0a6d98638e7acb774aac3af2550777accd8149c90af5b014e6d44d8" - url: "https://pub.dev" - source: hosted - version: "3.1.0" glob: dependency: transitive description: diff --git a/examples/movie_app/stac/app_theme.dart b/examples/movie_app/stac/app_theme.dart deleted file mode 100644 index 6dc341341..000000000 --- a/examples/movie_app/stac/app_theme.dart +++ /dev/null @@ -1,126 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:stac/src/parsers/theme/stac_color_scheme/stac_color_scheme.dart'; -import 'package:stac/src/parsers/theme/stac_divider_theme_data/stac_divider_theme_data.dart'; -import 'package:stac/src/parsers/theme/stac_text_theme/stac_text_theme.dart'; -import 'package:stac/src/parsers/theme/stac_theme/stac_theme.dart'; -import 'package:stac_core/stac_core.dart' - hide StacTheme; // Hide StacTheme from stac_core to use the one from stac - -/// Dark theme for the Movie App. -StacTheme get darkTheme { - return StacTheme( - brightness: Brightness.dark, - colorScheme: StacColorScheme( - brightness: Brightness.dark, - primary: '#95E183', - onPrimary: '#050608', - secondary: '#95E183', - onSecondary: '#FFFFFF', - surface: '#050608', - onSurface: '#FFFFFF', - onSurfaceVariant: '#65FFFFFF', - error: '#FF6565', - onError: '#050608', - outline: '#08FFFFFF', - ), - textTheme: StacTextTheme( - displayLarge: StacCustomTextStyle( - fontSize: 48, - fontWeight: StacFontWeight.w700, - height: 1.1, - ), - displayMedium: StacCustomTextStyle( - fontSize: 40, - fontWeight: StacFontWeight.w700, - height: 1.1, - ), - displaySmall: StacCustomTextStyle( - fontSize: 34, - fontWeight: StacFontWeight.w700, - height: 1.1, - ), - headlineLarge: StacCustomTextStyle( - fontSize: 30, - fontWeight: StacFontWeight.w700, - height: 1.3, - ), - headlineMedium: StacCustomTextStyle( - fontSize: 26, - fontWeight: StacFontWeight.w700, - height: 1.3, - ), - headlineSmall: StacCustomTextStyle( - fontSize: 23, - fontWeight: StacFontWeight.w700, - height: 1.3, - ), - titleLarge: StacCustomTextStyle( - fontSize: 20, - fontWeight: StacFontWeight.w500, - height: 1.3, - ), - titleMedium: StacCustomTextStyle( - fontSize: 18, - fontWeight: StacFontWeight.w500, - height: 1.3, - ), - titleSmall: StacCustomTextStyle( - fontSize: 16, - fontWeight: StacFontWeight.w500, - height: 1.3, - ), - labelLarge: StacCustomTextStyle( - fontSize: 16, - fontWeight: StacFontWeight.w700, - height: 1.3, - ), - labelMedium: StacCustomTextStyle( - fontSize: 14, - fontWeight: StacFontWeight.w600, - height: 1.3, - ), - labelSmall: StacCustomTextStyle( - fontSize: 12, - fontWeight: StacFontWeight.w500, - height: 1.3, - ), - bodyLarge: StacCustomTextStyle( - fontSize: 18, - fontWeight: StacFontWeight.w400, - height: 1.5, - ), - bodyMedium: StacCustomTextStyle( - fontSize: 16, - fontWeight: StacFontWeight.w400, - height: 1.5, - ), - bodySmall: StacCustomTextStyle( - fontSize: 14, - fontWeight: StacFontWeight.w400, - height: 1.5, - ), - ), - filledButtonTheme: StacButtonStyle( - minimumSize: StacSize(120, 40), - textStyle: StacCustomTextStyle( - fontSize: 16, - fontWeight: StacFontWeight.w500, - height: 1.3, - ), - padding: StacEdgeInsets.only(left: 10, right: 10, top: 8, bottom: 8), - shape: StacRoundedRectangleBorder(borderRadius: StacBorderRadius.all(8)), - ), - outlinedButtonTheme: StacButtonStyle( - minimumSize: StacSize(120, 40), - textStyle: StacCustomTextStyle( - fontSize: 16, - fontWeight: StacFontWeight.w500, - height: 1.3, - ), - padding: StacEdgeInsets.only(left: 10, right: 10, top: 8, bottom: 8), - side: StacBorderSide(color: '#95E183', width: 1.0), - shape: StacRoundedRectangleBorder(borderRadius: StacBorderRadius.all(8)), - ), - dividerTheme: StacDividerThemeData(color: '#24FFFFFF', thickness: 1), - ); -} diff --git a/examples/movie_app/stac/detail_screen.dart b/examples/movie_app/stac/detail_screen.dart index 91ecea146..80e2084db 100644 --- a/examples/movie_app/stac/detail_screen.dart +++ b/examples/movie_app/stac/detail_screen.dart @@ -63,7 +63,7 @@ StacWidget detailScreen() { StacExpanded( child: StacText( data: '{{title}}', - style: StacTheme.textTheme.headlineMedium, + style: StacThemeData.textTheme.headlineMedium, overflow: StacTextOverflow.ellipsis, ), ), @@ -98,7 +98,7 @@ StacWidget detailScreen() { StacDivider(), StacText( data: '{{release_date}} · {{runtime}} mins', - style: StacTheme.textTheme.bodySmall, + style: StacThemeData.textTheme.bodySmall, textAlign: StacTextAlign.left, ), StacDivider(), @@ -137,7 +137,7 @@ StacWidget detailScreen() { children: [ StacText( data: AppStrings.about, - style: StacTheme.textTheme.bodyMedium, + style: StacThemeData.textTheme.bodyMedium, ), StacSizedBox(height: 4), StacContainer(width: 24, height: 2, color: 'primary'), @@ -146,7 +146,7 @@ StacWidget detailScreen() { StacSizedBox(height: 20), StacText( data: '{{overview}}', - style: StacTheme.textTheme.bodyMedium, + style: StacThemeData.textTheme.bodyMedium, ), StacSizedBox(height: 24), StacColumn( @@ -223,34 +223,35 @@ StacWidget _buildCastListViewTemplate() { 'scrollDirection': 'horizontal', 'shrinkWrap': true, 'separator': StacSizedBox(width: 16).toJson(), - 'itemTemplate': StacSizedBox( - width: 80, - child: StacColumn( - crossAxisAlignment: StacCrossAxisAlignment.start, - children: [ - StacClipRRect( - borderRadius: StacBorderRadius.all(6), - child: StacImage( - src: '${AppApi.imageBaseUrl}/{{profile_path}}', - fit: StacBoxFit.cover, - width: 80, - height: 96, - ), - ), - StacSizedBox(height: 8), - StacText( - data: '{{name}}', - style: StacTheme.textTheme.titleSmall, - overflow: StacTextOverflow.ellipsis, - ), - StacText( - data: '{{character}}', - style: StacTheme.textTheme.bodySmall, - overflow: StacTextOverflow.ellipsis, + 'itemTemplate': + StacSizedBox( + width: 80, + child: StacColumn( + crossAxisAlignment: StacCrossAxisAlignment.start, + children: [ + StacClipRRect( + borderRadius: StacBorderRadius.all(6), + child: StacImage( + src: '${AppApi.imageBaseUrl}/{{profile_path}}', + fit: StacBoxFit.cover, + width: 80, + height: 96, + ), + ), + StacSizedBox(height: 8), + StacText( + data: '{{name}}', + style: StacThemeData.textTheme.titleSmall, + overflow: StacTextOverflow.ellipsis, + ), + StacText( + data: '{{character}}', + style: StacThemeData.textTheme.bodySmall, + overflow: StacTextOverflow.ellipsis, + ), + ], ), - ], - ), - ).toJson(), + ).toJson(), }; return StacWidget(jsonData: templateJson); @@ -263,27 +264,28 @@ StacWidget _buildSimilarMoviesListViewTemplate() { 'scrollDirection': 'horizontal', 'shrinkWrap': true, 'separator': StacSizedBox(width: 8).toJson(), - 'itemTemplate': StacGestureDetector( - onTap: StacAction.fromJson({ - 'actionType': 'setValue', - 'values': [ - {'key': 'movie_id', 'value': '{{data.id}}'}, - ], - 'action': { - 'actionType': 'navigate', - 'assetPath': AppAssets.detailScreenJson, - }, - }), - child: StacClipRRect( - borderRadius: StacBorderRadius.all(6), - child: StacImage( - imageType: StacImageType.network, - src: '${AppApi.imageBaseUrl}/{{data.poster_path}}', - width: 108, - height: 164, - ), - ), - ).toJson(), + 'itemTemplate': + StacGestureDetector( + onTap: StacAction.fromJson({ + 'actionType': 'setValue', + 'values': [ + {'key': 'movie_id', 'value': '{{data.id}}'}, + ], + 'action': { + 'actionType': 'navigate', + 'assetPath': AppAssets.detailScreenJson, + }, + }), + child: StacClipRRect( + borderRadius: StacBorderRadius.all(6), + child: StacImage( + imageType: StacImageType.network, + src: '${AppApi.imageBaseUrl}/{{data.poster_path}}', + width: 108, + height: 164, + ), + ), + ).toJson(), }; return StacWidget(jsonData: templateJson); diff --git a/examples/movie_app/stac/home_screen.dart b/examples/movie_app/stac/home_screen.dart index 1227f4ce3..ca329de7a 100644 --- a/examples/movie_app/stac/home_screen.dart +++ b/examples/movie_app/stac/home_screen.dart @@ -33,7 +33,7 @@ StacWidget homeScreen() { children: [ StacText( data: AppStrings.nowPlaying, - style: StacTheme.textTheme.labelLarge, + style: StacThemeData.textTheme.labelLarge, ), ], ), @@ -65,7 +65,7 @@ StacWidget homeScreen() { children: [ StacText( data: AppStrings.popularMovies, - style: StacTheme.textTheme.labelLarge, + style: StacThemeData.textTheme.labelLarge, ), ], ), @@ -97,7 +97,7 @@ StacWidget homeScreen() { children: [ StacText( data: AppStrings.trendingMovies, - style: StacTheme.textTheme.labelLarge, + style: StacThemeData.textTheme.labelLarge, ), ], ), @@ -129,7 +129,7 @@ StacWidget homeScreen() { children: [ StacText( data: AppStrings.topRated, - style: StacTheme.textTheme.labelLarge, + style: StacThemeData.textTheme.labelLarge, ), ], ), @@ -161,7 +161,7 @@ StacWidget homeScreen() { children: [ StacText( data: AppStrings.upcomingMovies, - style: StacTheme.textTheme.labelLarge, + style: StacThemeData.textTheme.labelLarge, ), ], ), @@ -217,28 +217,24 @@ StacWidget _buildMovieListViewTemplate() { 'shrinkWrap': true, 'separator': StacSizedBox(width: 8).toJson(), 'padding': StacEdgeInsets.only(left: 16).toJson(), - 'itemTemplate': - StacGestureDetector( - onTap: StacAction.fromJson({ - 'actionType': 'setValue', - 'values': [ - {'key': 'movie_id', 'value': '{{id}}'}, - ], - 'action': { - 'actionType': 'navigate', - 'assetPath': AppAssets.detailScreenJson, - }, - }), - child: StacClipRRect( - borderRadius: StacBorderRadius.all(6), - child: StacImage( - imageType: StacImageType.network, - src: '${AppApi.imageBaseUrl}/{{poster_path}}', - width: 108, - height: 164, - ), - ), - ).toJson(), + 'itemTemplate': StacGestureDetector( + onTap: StacAction.fromJson({ + 'actionType': 'setValue', + 'values': [ + {'key': 'movie_id', 'value': '{{id}}'}, + ], + 'action': {'actionType': 'navigate', 'routeName': 'detail_screen'}, + }), + child: StacClipRRect( + borderRadius: StacBorderRadius.all(6), + child: StacImage( + imageType: StacImageType.network, + src: '${AppApi.imageBaseUrl}/{{poster_path}}', + width: 108, + height: 164, + ), + ), + ).toJson(), }; // Create a StacWidget with the JSON data diff --git a/examples/movie_app/stac/onboarding_screen.dart b/examples/movie_app/stac/onboarding_screen.dart index 4f34c7472..748f72a52 100644 --- a/examples/movie_app/stac/onboarding_screen.dart +++ b/examples/movie_app/stac/onboarding_screen.dart @@ -41,7 +41,7 @@ StacWidget onboardingScreen() { children: [ StacText( data: AppStrings.onboardingTitle, - style: StacTheme.textTheme.displayMedium, + style: StacThemeData.textTheme.displayMedium, children: [ StacTextSpan( text: AppStrings.onboardingTitleAccent, @@ -52,7 +52,7 @@ StacWidget onboardingScreen() { StacSizedBox(height: 24), StacText( data: AppStrings.onboardingDescription, - style: StacTheme.textTheme.bodyMedium, + style: StacThemeData.textTheme.bodyMedium, ), StacSizedBox(height: 64), StacSizedBox( @@ -62,9 +62,7 @@ StacWidget onboardingScreen() { child: StacText( data: AppStrings.onboardingGetStartedButton, ), - onPressed: StacNavigateAction( - assetPath: AppAssets.homeScreenJson, - ), + onPressed: StacNavigateAction(routeName: 'home_screen'), ), ), ], From a07a82fea54243068f93118d15b52eda604d3693 Mon Sep 17 00:00:00 2001 From: Divyanshu Bhargava Date: Sat, 8 Nov 2025 01:12:12 +0530 Subject: [PATCH 3/5] refactor: Improve JSON serialization and update styling in movie app screens --- examples/movie_app/stac/detail_screen.dart | 95 +++++++++---------- .../movie_app/stac/onboarding_screen.dart | 2 +- 2 files changed, 46 insertions(+), 51 deletions(-) diff --git a/examples/movie_app/stac/detail_screen.dart b/examples/movie_app/stac/detail_screen.dart index 80e2084db..e6ca7ba63 100644 --- a/examples/movie_app/stac/detail_screen.dart +++ b/examples/movie_app/stac/detail_screen.dart @@ -223,35 +223,34 @@ StacWidget _buildCastListViewTemplate() { 'scrollDirection': 'horizontal', 'shrinkWrap': true, 'separator': StacSizedBox(width: 16).toJson(), - 'itemTemplate': - StacSizedBox( - width: 80, - child: StacColumn( - crossAxisAlignment: StacCrossAxisAlignment.start, - children: [ - StacClipRRect( - borderRadius: StacBorderRadius.all(6), - child: StacImage( - src: '${AppApi.imageBaseUrl}/{{profile_path}}', - fit: StacBoxFit.cover, - width: 80, - height: 96, - ), - ), - StacSizedBox(height: 8), - StacText( - data: '{{name}}', - style: StacThemeData.textTheme.titleSmall, - overflow: StacTextOverflow.ellipsis, - ), - StacText( - data: '{{character}}', - style: StacThemeData.textTheme.bodySmall, - overflow: StacTextOverflow.ellipsis, - ), - ], + 'itemTemplate': StacSizedBox( + width: 80, + child: StacColumn( + crossAxisAlignment: StacCrossAxisAlignment.start, + children: [ + StacClipRRect( + borderRadius: StacBorderRadius.all(6), + child: StacImage( + src: '${AppApi.imageBaseUrl}/{{profile_path}}', + fit: StacBoxFit.cover, + width: 80, + height: 96, + ), + ), + StacSizedBox(height: 8), + StacText( + data: '{{name}}', + style: StacThemeData.textTheme.titleSmall, + overflow: StacTextOverflow.ellipsis, ), - ).toJson(), + StacText( + data: '{{character}}', + style: StacThemeData.textTheme.bodySmall, + overflow: StacTextOverflow.ellipsis, + ), + ], + ), + ).toJson(), }; return StacWidget(jsonData: templateJson); @@ -264,28 +263,24 @@ StacWidget _buildSimilarMoviesListViewTemplate() { 'scrollDirection': 'horizontal', 'shrinkWrap': true, 'separator': StacSizedBox(width: 8).toJson(), - 'itemTemplate': - StacGestureDetector( - onTap: StacAction.fromJson({ - 'actionType': 'setValue', - 'values': [ - {'key': 'movie_id', 'value': '{{data.id}}'}, - ], - 'action': { - 'actionType': 'navigate', - 'assetPath': AppAssets.detailScreenJson, - }, - }), - child: StacClipRRect( - borderRadius: StacBorderRadius.all(6), - child: StacImage( - imageType: StacImageType.network, - src: '${AppApi.imageBaseUrl}/{{data.poster_path}}', - width: 108, - height: 164, - ), - ), - ).toJson(), + 'itemTemplate': StacGestureDetector( + onTap: StacAction.fromJson({ + 'actionType': 'setValue', + 'values': [ + {'key': 'movie_id', 'value': '{{data.id}}'}, + ], + 'action': {'actionType': 'navigate', 'routeName': 'detail_screen'}, + }), + child: StacClipRRect( + borderRadius: StacBorderRadius.all(6), + child: StacImage( + imageType: StacImageType.network, + src: '${AppApi.imageBaseUrl}/{{data.poster_path}}', + width: 108, + height: 164, + ), + ), + ).toJson(), }; return StacWidget(jsonData: templateJson); diff --git a/examples/movie_app/stac/onboarding_screen.dart b/examples/movie_app/stac/onboarding_screen.dart index 748f72a52..b5308c682 100644 --- a/examples/movie_app/stac/onboarding_screen.dart +++ b/examples/movie_app/stac/onboarding_screen.dart @@ -45,7 +45,7 @@ StacWidget onboardingScreen() { children: [ StacTextSpan( text: AppStrings.onboardingTitleAccent, - style: StacCustomTextStyle(color: 'primary'), + style: StacTextStyle(color: StacColors.primary), ), ], ), From a0ea1705370154c53ce27296cc6c9a511c0fbb21 Mon Sep 17 00:00:00 2001 From: Divyanshu Bhargava Date: Sat, 8 Nov 2025 01:46:46 +0530 Subject: [PATCH 4/5] chore: Remove unnecessary blank line in default_stac_options.dart --- examples/movie_app/lib/default_stac_options.dart | 1 - 1 file changed, 1 deletion(-) diff --git a/examples/movie_app/lib/default_stac_options.dart b/examples/movie_app/lib/default_stac_options.dart index 3fe4aa7e9..84eb0d686 100644 --- a/examples/movie_app/lib/default_stac_options.dart +++ b/examples/movie_app/lib/default_stac_options.dart @@ -1,4 +1,3 @@ - // This file is automatically generated by stac init. import 'package:stac_core/core/stac_options.dart'; From c042538069b7bb1deefe587426c47481a4d6cebd Mon Sep 17 00:00:00 2001 From: Divyanshu Bhargava Date: Sat, 8 Nov 2025 01:50:22 +0530 Subject: [PATCH 5/5] refactor: Update image dimensions to use double.maxFinite in movie app screens --- examples/movie_app/stac/detail_screen.dart | 2 +- examples/movie_app/stac/onboarding_screen.dart | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/examples/movie_app/stac/detail_screen.dart b/examples/movie_app/stac/detail_screen.dart index e6ca7ba63..b3557d0d3 100644 --- a/examples/movie_app/stac/detail_screen.dart +++ b/examples/movie_app/stac/detail_screen.dart @@ -30,7 +30,7 @@ StacWidget detailScreen() { children: [ StacImage( src: '${AppApi.imageBaseUrl}/{{poster_path}}', - width: 1000, + width: double.maxFinite, height: 480, fit: StacBoxFit.cover, ), diff --git a/examples/movie_app/stac/onboarding_screen.dart b/examples/movie_app/stac/onboarding_screen.dart index b5308c682..ada942fc5 100644 --- a/examples/movie_app/stac/onboarding_screen.dart +++ b/examples/movie_app/stac/onboarding_screen.dart @@ -9,8 +9,8 @@ StacWidget onboardingScreen() { StacImage( imageType: StacImageType.asset, src: AppAssets.onboardingImage, - width: 10000, - height: 10000, + width: double.maxFinite, + height: double.maxFinite, fit: StacBoxFit.cover, ), StacPositioned( @@ -18,7 +18,7 @@ StacWidget onboardingScreen() { right: 0, bottom: 0, child: StacContainer( - width: 1000, + width: double.maxFinite, height: 500, decoration: StacBoxDecoration( gradient: StacGradient.linear( @@ -57,7 +57,7 @@ StacWidget onboardingScreen() { StacSizedBox(height: 64), StacSizedBox( height: 48, - width: 1000, + width: double.maxFinite, child: StacFilledButton( child: StacText( data: AppStrings.onboardingGetStartedButton,