diff --git a/.gitignore b/.gitignore index 24476c5..c892a5a 100644 --- a/.gitignore +++ b/.gitignore @@ -31,6 +31,9 @@ migrate_working_dir/ .pub-cache/ .pub/ /build/ +*.g.dart +*.gen.dart +pubspec.lock # Symbolication related app.*.symbols diff --git a/README.md b/README.md index 6830e95..9635367 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,23 @@ # flutter_movie_clean A sample Flutter application follow clean architecture + +## Installation +Clone this repository +```bash +git clone git@github.com:namnh-0652/flutter_movie_clean.git +``` +Install Pub +```bash +flutter clean +flutter pub get +``` + +Run command to add file `locale_keys.g.dart`, this file support for library `easy_localization` +```bash +flutter pub run easy_localization:generate -S assets/translations -f keys -o locale_keys.g.dart +``` + +Run command to run code generator for your assets, fonts, colors... by `FlutterGen` +```bash +flutter packages pub run build_runner build +``` diff --git a/assets/color/colors.xml b/assets/color/colors.xml new file mode 100644 index 0000000..e584525 --- /dev/null +++ b/assets/color/colors.xml @@ -0,0 +1,4 @@ + + + #ED1B24 + diff --git a/assets/images/bg_profile.png b/assets/images/bg_profile.png new file mode 100644 index 0000000..52b2aec Binary files /dev/null and b/assets/images/bg_profile.png differ diff --git a/assets/images/icons/ic_categories.png b/assets/images/icons/ic_categories.png new file mode 100644 index 0000000..94f95fa Binary files /dev/null and b/assets/images/icons/ic_categories.png differ diff --git a/assets/images/icons/ic_downloads.png b/assets/images/icons/ic_downloads.png new file mode 100644 index 0000000..dc8fa25 Binary files /dev/null and b/assets/images/icons/ic_downloads.png differ diff --git a/assets/images/icons/ic_home.png b/assets/images/icons/ic_home.png new file mode 100644 index 0000000..afd1db9 Binary files /dev/null and b/assets/images/icons/ic_home.png differ diff --git a/assets/images/icons/ic_more.png b/assets/images/icons/ic_more.png new file mode 100644 index 0000000..ae31d53 Binary files /dev/null and b/assets/images/icons/ic_more.png differ diff --git a/assets/images/marvel_logo.png b/assets/images/marvel_logo.png new file mode 100644 index 0000000..81ebc0d Binary files /dev/null and b/assets/images/marvel_logo.png differ diff --git a/assets/images/movies/movies1.png b/assets/images/movies/movies1.png new file mode 100644 index 0000000..b9eed26 Binary files /dev/null and b/assets/images/movies/movies1.png differ diff --git a/assets/images/movies/movies2.png b/assets/images/movies/movies2.png new file mode 100644 index 0000000..4949d68 Binary files /dev/null and b/assets/images/movies/movies2.png differ diff --git a/assets/images/movies/movies3.png b/assets/images/movies/movies3.png new file mode 100644 index 0000000..f6aa657 Binary files /dev/null and b/assets/images/movies/movies3.png differ diff --git a/assets/images/onboardings/poster1.jpeg b/assets/images/onboardings/poster1.jpeg new file mode 100644 index 0000000..c939a77 Binary files /dev/null and b/assets/images/onboardings/poster1.jpeg differ diff --git a/assets/images/onboardings/poster2.jpeg b/assets/images/onboardings/poster2.jpeg new file mode 100644 index 0000000..e98570f Binary files /dev/null and b/assets/images/onboardings/poster2.jpeg differ diff --git a/assets/images/onboardings/poster3.jpeg b/assets/images/onboardings/poster3.jpeg new file mode 100644 index 0000000..92589b9 Binary files /dev/null and b/assets/images/onboardings/poster3.jpeg differ diff --git a/assets/images/onboardings/poster4.jpeg b/assets/images/onboardings/poster4.jpeg new file mode 100644 index 0000000..a80118e Binary files /dev/null and b/assets/images/onboardings/poster4.jpeg differ diff --git a/assets/images/onboardings/poster5.jpeg b/assets/images/onboardings/poster5.jpeg new file mode 100644 index 0000000..0be770e Binary files /dev/null and b/assets/images/onboardings/poster5.jpeg differ diff --git a/assets/images/onboardings/poster6.jpeg b/assets/images/onboardings/poster6.jpeg new file mode 100644 index 0000000..4f24616 Binary files /dev/null and b/assets/images/onboardings/poster6.jpeg differ diff --git a/assets/images/profile_avatar.png b/assets/images/profile_avatar.png new file mode 100644 index 0000000..053d390 Binary files /dev/null and b/assets/images/profile_avatar.png differ diff --git a/assets/images/series/series1.png b/assets/images/series/series1.png new file mode 100644 index 0000000..635cc6b Binary files /dev/null and b/assets/images/series/series1.png differ diff --git a/assets/images/series/series2.png b/assets/images/series/series2.png new file mode 100644 index 0000000..559a77b Binary files /dev/null and b/assets/images/series/series2.png differ diff --git a/assets/images/series/series3.png b/assets/images/series/series3.png new file mode 100644 index 0000000..f953d81 Binary files /dev/null and b/assets/images/series/series3.png differ diff --git a/assets/images/trendings/trending_1.png b/assets/images/trendings/trending_1.png new file mode 100644 index 0000000..40ebe7e Binary files /dev/null and b/assets/images/trendings/trending_1.png differ diff --git a/assets/images/trendings/trending_2.png b/assets/images/trendings/trending_2.png new file mode 100644 index 0000000..e56ed74 Binary files /dev/null and b/assets/images/trendings/trending_2.png differ diff --git a/assets/images/trendings/trending_3.png b/assets/images/trendings/trending_3.png new file mode 100644 index 0000000..262c398 Binary files /dev/null and b/assets/images/trendings/trending_3.png differ diff --git a/assets/images/trendings/trending_4.png b/assets/images/trendings/trending_4.png new file mode 100644 index 0000000..bb12558 Binary files /dev/null and b/assets/images/trendings/trending_4.png differ diff --git a/assets/translations/en.json b/assets/translations/en.json new file mode 100644 index 0000000..42e64d2 --- /dev/null +++ b/assets/translations/en.json @@ -0,0 +1,19 @@ +{ + "continueOnBoarding": "Continue", + "login": "Login", + "signup": "Signup", + "onBoardingPageFirstTitle": "All your favorite \n MARVEL Movies & Series \n at one place", + "onBoardingPageSecondTitle": "Watch Online \n or \n Download Offline", + "onBoardingPageThirdTitle": "Create profiles for \n different members & \n get personalised \n recommendations", + "onBoardingPageFourthTitle": "Plans according to your \n needs at affordable \n prices", + "onBoardingPageFifthTitle": "Let's Get Started !!!", + "home": "Home", + "categories": "Categories", + "downloads": "Downloads", + "more": "More", + "latestMovies": "Latest Movies", + "latestSeries": "Latest Series", + "trendingToday": "Trending Today", + "oldMovies": "Old Movies", + "oldSeries": "Old Series" +} diff --git a/assets/translations/vi.json b/assets/translations/vi.json new file mode 100644 index 0000000..6832733 --- /dev/null +++ b/assets/translations/vi.json @@ -0,0 +1,19 @@ +{ + "continueOnBoarding": "Tiếp tục", + "login": "Đăng nhập", + "signup": "Đăng ký", + "onBoardingPageFirstTitle": "Tất cả \n phim MARVEL & Series \n tại một nơi", + "onBoardingPageSecondTitle": "Xem trực tuyến \n hoặc \n tải xuống ngoại tuyến", + "onBoardingPageThirdTitle": "Tạo hồ sơ cho \n các thành viên khác nhau & \n nhận đề xuất \n được cá nhân hóa", + "onBoardingPageFourthTitle": "Các gói theo nhu cầu của bạn \n với mức giá phải chăng", + "onBoardingPageFifthTitle": "Bắt đầu thôi !!!", + "home": "Trang chủ", + "categories": "Thể loại", + "downloads": "Tải xuống", + "more": "Thêm", + "latestMovies": "Những Bộ Phim Gần Đây", + "latestSeries": "Sê-ri Mới Nhất", + "trendingToday": "Xu Hướng Hôm Nay", + "oldMovies": "Những Bộ Phim Cũ", + "oldSeries": "Sê-ri Cũ" +} diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist index 6397623..b36673f 100644 --- a/ios/Runner/Info.plist +++ b/ios/Runner/Info.plist @@ -4,6 +4,13 @@ CFBundleDevelopmentRegion $(DEVELOPMENT_LANGUAGE) + + CFBundleLocalizations + + en + vi + + CFBundleDisplayName Flutter Movie Clean CFBundleExecutable diff --git a/lib/bloc_observer.dart b/lib/bloc_observer.dart new file mode 100644 index 0000000..efdb35d --- /dev/null +++ b/lib/bloc_observer.dart @@ -0,0 +1,29 @@ +import 'package:flutter_bloc/flutter_bloc.dart'; + +class AppBlocObserver extends BlocObserver { + const AppBlocObserver(); + + @override + void onEvent(Bloc bloc, Object? event) { + super.onEvent(bloc, event); + print(event); + } + + @override + void onError(BlocBase bloc, Object error, StackTrace stackTrace) { + super.onError(bloc, error, stackTrace); + print(error); + } + + @override + void onChange(BlocBase bloc, Change change) { + super.onChange(bloc, change); + print(change); + } + + @override + void onTransition(Bloc bloc, Transition transition) { + super.onTransition(bloc, transition); + print(transition); + } +} diff --git a/lib/categories/categories.dart b/lib/categories/categories.dart new file mode 100644 index 0000000..2a5ae81 --- /dev/null +++ b/lib/categories/categories.dart @@ -0,0 +1,10 @@ +import 'package:flutter/material.dart'; + +class CategoriesScreen extends StatelessWidget { + const CategoriesScreen({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return const Center(child: Text("Categories"),); + } +} diff --git a/lib/downloads/downloads.dart b/lib/downloads/downloads.dart new file mode 100644 index 0000000..bfc4264 --- /dev/null +++ b/lib/downloads/downloads.dart @@ -0,0 +1,10 @@ +import 'package:flutter/material.dart'; + +class DownloadsScreen extends StatelessWidget { + const DownloadsScreen({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return const Center(child: Text("Download"),); + } +} diff --git a/lib/exports/screens.dart b/lib/exports/screens.dart new file mode 100644 index 0000000..2cedf95 --- /dev/null +++ b/lib/exports/screens.dart @@ -0,0 +1,6 @@ +export '/categories/categories.dart'; +export '/downloads/downloads.dart'; +export '/home/home_page.dart'; +export '/more/more.dart'; +export '/moviedetails/movie_details.dart'; +export '/onboarding/on_boarding.dart'; diff --git a/lib/home/components/carousel_slider_shader.dart b/lib/home/components/carousel_slider_shader.dart new file mode 100644 index 0000000..bec82b1 --- /dev/null +++ b/lib/home/components/carousel_slider_shader.dart @@ -0,0 +1,51 @@ +import 'package:carousel_slider/carousel_slider.dart'; +import 'package:flutter/material.dart'; + +class CarouselSliderShaderWidget extends StatelessWidget { + const CarouselSliderShaderWidget({ + super.key, + required List paths, required this.onItemTapped, + }) : _paths = paths; + + final Gradient _maskingGradient = const LinearGradient( + colors: [ + Colors.black, + Colors.transparent, + ], + stops: [0.0, 0.4], + tileMode: TileMode.mirror, + begin: Alignment.centerLeft, + end: Alignment.center, + ); + final List _paths; + final Function(int index) onItemTapped; + + @override + Widget build(BuildContext context) { + return ShaderMask( + shaderCallback: (bounds) => _maskingGradient.createShader(bounds), + blendMode: BlendMode.darken, + child: CarouselSlider( + options: CarouselOptions( + height: 173.0, + enlargeFactor: 0.2, + viewportFraction: 0.7, + enlargeCenterPage: true, + ), + items: _paths.map((path) { + return Builder(builder: (BuildContext context) { + return GestureDetector( + child: Container( + width: MediaQuery.of(context).size.width, + margin: const EdgeInsets.symmetric(horizontal: 6.0), + decoration: const BoxDecoration(color: Colors.amber), + child: Image.asset(path, fit: BoxFit.fill), + ), + onTap: () {onItemTapped(_paths.indexOf(path));}, + ); + }); + }).toList(), + ), + ); + } +} diff --git a/lib/home/components/horizontal_movie_list_view.dart b/lib/home/components/horizontal_movie_list_view.dart new file mode 100644 index 0000000..2d32984 --- /dev/null +++ b/lib/home/components/horizontal_movie_list_view.dart @@ -0,0 +1,40 @@ +import 'package:flutter/material.dart'; + +class HorizontalMoviesListView extends StatelessWidget { + const HorizontalMoviesListView({ + super.key, + required List paths, + required this.itemWidth, + required this.itemHeight, + required this.onItemTapped, + }) : _paths = paths; + + final List _paths; + final double itemWidth; + final double itemHeight; + final Function(int index) onItemTapped; + + @override + Widget build(BuildContext context) { + return ListView.builder( + physics: const ClampingScrollPhysics(), + shrinkWrap: true, + scrollDirection: Axis.horizontal, + itemCount: _paths.length, + itemBuilder: (BuildContext context, int index) => Padding( + padding: const EdgeInsets.only( + left: 12.0, + ), + child: GestureDetector( + child: Image.asset( + _paths.elementAt(index), + fit: BoxFit.cover, + width: itemWidth, + height: itemHeight, + ), + onTap: () => {onItemTapped(index)}, + ), + ), + ); + } +} diff --git a/lib/home/cubit/home_cubit.dart b/lib/home/cubit/home_cubit.dart new file mode 100644 index 0000000..6a18f4b --- /dev/null +++ b/lib/home/cubit/home_cubit.dart @@ -0,0 +1,6 @@ +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_movie_clean/home/cubit/home_state.dart'; + +class HomeCubit extends Cubit { + HomeCubit() : super(HomeState()); +} diff --git a/lib/home/cubit/home_state.dart b/lib/home/cubit/home_state.dart new file mode 100644 index 0000000..7aad774 --- /dev/null +++ b/lib/home/cubit/home_state.dart @@ -0,0 +1,28 @@ +import '../../generated/assets.gen.dart'; + +class HomeState { + HomeState(); + + final List moviesPaths = [ + Assets.images.movies.movies1.path, + Assets.images.movies.movies2.path, + Assets.images.movies.movies3.path, + ]; + + final List seriesPaths = [ + Assets.images.series.series1.path, + Assets.images.series.series2.path, + Assets.images.series.series3.path, + ]; + + final List trendingPaths = [ + Assets.images.trendings.trending1.path, + Assets.images.trendings.trending2.path, + Assets.images.trendings.trending3.path, + Assets.images.trendings.trending4.path, + Assets.images.trendings.trending1.path, + Assets.images.trendings.trending2.path, + Assets.images.trendings.trending3.path, + Assets.images.trendings.trending4.path, + ]; +} diff --git a/lib/home/home_page.dart b/lib/home/home_page.dart new file mode 100644 index 0000000..5ca06ae --- /dev/null +++ b/lib/home/home_page.dart @@ -0,0 +1,17 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +import 'cubit/home_cubit.dart'; +import 'home_view.dart'; + +class HomeScreen extends StatelessWidget { + const HomeScreen({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return BlocProvider( + create: (_) => HomeCubit(), + child: const HomeView(), + ); + } +} diff --git a/lib/home/home_view.dart b/lib/home/home_view.dart new file mode 100644 index 0000000..808d920 --- /dev/null +++ b/lib/home/home_view.dart @@ -0,0 +1,109 @@ +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_movie_clean/widgets/header_widget.dart'; +import 'package:go_router/go_router.dart'; + +import '../generated/locale_keys.g.dart'; +import 'components/carousel_slider_shader.dart'; +import 'components/horizontal_movie_list_view.dart'; +import 'cubit/home_cubit.dart'; + +class HomeView extends StatelessWidget { + const HomeView({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + final state = BlocProvider.of(context, listen: false).state; + return SingleChildScrollView( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const SizedBox(height: 16.0), + HeaderWidget(onProfileTapped: () {}), + Padding( + padding: const EdgeInsets.only(left: 12.0, bottom: 14.0), + child: Text( + LocaleKeys.latestMovies.tr(), + style: Theme.of(context).textTheme.titleLarge, + ), + ), + CarouselSliderShaderWidget( + paths: state.moviesPaths, + onItemTapped: (int index) { + context.go(context.namedLocation('movie_details')); + }, + ), + Padding( + padding: const EdgeInsets.only(top: 24.0, left: 12.0, bottom: 14.0), + child: Text( + LocaleKeys.latestSeries.tr(), + style: Theme.of(context).textTheme.titleLarge, + ), + ), + CarouselSliderShaderWidget( + paths: state.seriesPaths, + onItemTapped: (int index) { + context.go(context.namedLocation('movie_details')); + }, + ), + Padding( + padding: const EdgeInsets.only(top: 24.0, left: 12.0, bottom: 14.0), + child: Text( + LocaleKeys.trendingToday.tr(), + style: Theme.of(context).textTheme.titleLarge, + ), + ), + SizedBox( + height: 150.0, + child: HorizontalMoviesListView( + paths: state.trendingPaths, + itemWidth: 100.0, + itemHeight: 150.0, + onItemTapped: (int index) { + context.go(context.namedLocation('movie_details')); + }, + ), + ), + Padding( + padding: const EdgeInsets.only(top: 24.0, left: 12.0, bottom: 14.0), + child: Text( + LocaleKeys.oldMovies.tr(), + style: Theme.of(context).textTheme.titleLarge, + ), + ), + SizedBox( + height: 150.0, + child: HorizontalMoviesListView( + paths: state.trendingPaths, + itemWidth: 100.0, + itemHeight: 150.0, + onItemTapped: (int index) { + context.go(context.namedLocation('movie_details')); + }, + ), + ), + Padding( + padding: const EdgeInsets.only(top: 24.0, left: 12.0, bottom: 14.0), + child: Text( + LocaleKeys.oldSeries.tr(), + style: Theme.of(context).textTheme.titleLarge, + ), + ), + SizedBox( + height: 60.0, + child: HorizontalMoviesListView( + paths: state.trendingPaths, + itemWidth: 100.0, + itemHeight: 60.0, + onItemTapped: (int index) { + context.go(context.namedLocation('movie_details')); + }, + ), + ), + const SizedBox(height: 65.0), + ], + ), + ); + } +} diff --git a/lib/main.dart b/lib/main.dart index 008fa38..9370f04 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,115 +1,41 @@ +import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; - -void main() { - runApp(const MyApp()); +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_movie_clean/bloc_observer.dart'; +import 'package:flutter_movie_clean/theme/app_theme.dart'; + +import 'router/router.dart'; + +void main() async { + WidgetsFlutterBinding.ensureInitialized(); + Bloc.observer = const AppBlocObserver(); + + await EasyLocalization.ensureInitialized(); + const supportedLocales = [Locale('en'), Locale('vi')]; + runApp( + EasyLocalization( + supportedLocales: supportedLocales, + path: 'assets/translations', + fallbackLocale: const Locale('en'), + useFallbackTranslations: true, + child: const MyApp(), + ), + ); } class MyApp extends StatelessWidget { const MyApp({super.key}); - // This widget is the root of your application. - @override - Widget build(BuildContext context) { - return MaterialApp( - title: 'Flutter Demo', - theme: ThemeData( - // This is the theme of your application. - // - // Try running your application with "flutter run". You'll see the - // application has a blue toolbar. Then, without quitting the app, try - // changing the primarySwatch below to Colors.green and then invoke - // "hot reload" (press "r" in the console where you ran "flutter run", - // or simply save your changes to "hot reload" in a Flutter IDE). - // Notice that the counter didn't reset back to zero; the application - // is not restarted. - primarySwatch: Colors.blue, - ), - home: const MyHomePage(title: 'Flutter Demo Home Page'), - ); - } -} - -class MyHomePage extends StatefulWidget { - const MyHomePage({super.key, required this.title}); - - // This widget is the home page of your application. It is stateful, meaning - // that it has a State object (defined below) that contains fields that affect - // how it looks. - - // This class is the configuration for the state. It holds the values (in this - // case the title) provided by the parent (in this case the App widget) and - // used by the build method of the State. Fields in a Widget subclass are - // always marked "final". - - final String title; - - @override - State createState() => _MyHomePageState(); -} - -class _MyHomePageState extends State { - int _counter = 0; - - void _incrementCounter() { - setState(() { - // This call to setState tells the Flutter framework that something has - // changed in this State, which causes it to rerun the build method below - // so that the display can reflect the updated values. If we changed - // _counter without calling setState(), then the build method would not be - // called again, and so nothing would appear to happen. - _counter++; - }); - } - @override Widget build(BuildContext context) { - // This method is rerun every time setState is called, for instance as done - // by the _incrementCounter method above. - // - // The Flutter framework has been optimized to make rerunning build methods - // fast, so that you can just rebuild anything that needs updating rather - // than having to individually change instances of widgets. - return Scaffold( - appBar: AppBar( - // Here we take the value from the MyHomePage object that was created by - // the App.build method, and use it to set our appbar title. - title: Text(widget.title), - ), - body: Center( - // Center is a layout widget. It takes a single child and positions it - // in the middle of the parent. - child: Column( - // Column is also a layout widget. It takes a list of children and - // arranges them vertically. By default, it sizes itself to fit its - // children horizontally, and tries to be as tall as its parent. - // - // Invoke "debug painting" (press "p" in the console, choose the - // "Toggle Debug Paint" action from the Flutter Inspector in Android - // Studio, or the "Toggle Debug Paint" command in Visual Studio Code) - // to see the wireframe for each widget. - // - // Column has various properties to control how it sizes itself and - // how it positions its children. Here we use mainAxisAlignment to - // center the children vertically; the main axis here is the vertical - // axis because Columns are vertical (the cross axis would be - // horizontal). - mainAxisAlignment: MainAxisAlignment.center, - children: [ - const Text( - 'You have pushed the button this many times:', - ), - Text( - '$_counter', - style: Theme.of(context).textTheme.headlineMedium, - ), - ], - ), - ), - floatingActionButton: FloatingActionButton( - onPressed: _incrementCounter, - tooltip: 'Increment', - child: const Icon(Icons.add), - ), // This trailing comma makes auto-formatting nicer for build methods. + final darkTheme = AppTheme.dark(); + return MaterialApp.router( + localizationsDelegates: context.localizationDelegates, + supportedLocales: context.supportedLocales, + locale: context.locale, + debugShowCheckedModeBanner: false, + theme: darkTheme, + routerConfig: router, ); } } diff --git a/lib/more/more.dart b/lib/more/more.dart new file mode 100644 index 0000000..2bda9cd --- /dev/null +++ b/lib/more/more.dart @@ -0,0 +1,10 @@ +import 'package:flutter/material.dart'; + +class MoreScreen extends StatelessWidget { + const MoreScreen({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return const Center(child: Text("More"),); + } +} diff --git a/lib/moviedetails/movie_details.dart b/lib/moviedetails/movie_details.dart new file mode 100644 index 0000000..06a6e53 --- /dev/null +++ b/lib/moviedetails/movie_details.dart @@ -0,0 +1,10 @@ +import 'package:flutter/material.dart'; + +class MovieDetailsScreen extends StatelessWidget { + const MovieDetailsScreen({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return const Center(child: Text('Movie details'),); + } +} diff --git a/lib/onboarding/components/on_boarding_end_page.dart b/lib/onboarding/components/on_boarding_end_page.dart new file mode 100644 index 0000000..e554b74 --- /dev/null +++ b/lib/onboarding/components/on_boarding_end_page.dart @@ -0,0 +1,112 @@ +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/material.dart'; +import 'package:go_router/go_router.dart'; +import 'package:smooth_page_indicator/smooth_page_indicator.dart'; + +import '../../generated/assets.gen.dart'; +import '../../generated/colors.gen.dart'; +import '../../generated/locale_keys.g.dart'; + +class OnBoardingEndPage extends StatelessWidget { + const OnBoardingEndPage({ + super.key, + required PageController controller, + required this.context, + }) : _controller = controller; + + final PageController _controller; + final BuildContext context; + + @override + Widget build(BuildContext context) { + return SizedBox.expand( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const SizedBox( + height: 132.0, + ), + IgnorePointer( + ignoring: true, + child: Image.asset( + Assets.images.marvelLogo.path, + width: 185.0, + height: 75.0, + fit: BoxFit.cover, + alignment: Alignment.center, + ), + ), + const SizedBox( + height: 60.0, + ), + IgnorePointer( + ignoring: true, + child: SmoothPageIndicator( + controller: _controller, + count: 6, + effect: const WormEffect( + activeDotColor: ColorName.crimsonApprox, + dotColor: Colors.white, + dotHeight: 10.0, + dotWidth: 10.0, + spacing: 10.0, + ), + ), + ), + const SizedBox( + height: 60.0, + ), + Padding( + padding: const EdgeInsets.only(left: 50.0, right: 50.0), + child: SizedBox( + width: double.infinity, + height: 50.0, + child: TextButton( + style: TextButton.styleFrom( + backgroundColor: ColorName.crimsonApprox, + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.all(Radius.zero), + ), + ), + onPressed: () {}, + child: Text( + LocaleKeys.signup.tr(), + style: Theme.of(context).textTheme.titleMedium, + ), + ), + ), + ), + const SizedBox( + height: 30.0, + ), + Padding( + padding: const EdgeInsets.only(left: 50.0, right: 50.0), + child: SizedBox( + width: double.infinity, + height: 50.0, + child: TextButton( + style: TextButton.styleFrom( + backgroundColor: Colors.transparent, + side: const BorderSide( + width: 3.0, + color: ColorName.crimsonApprox, + ), + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.all(Radius.zero), + ), + ), + onPressed: () { + context.go(context.namedLocation('home')); + }, + child: Text( + LocaleKeys.login.tr(), + style: Theme.of(context).textTheme.titleMedium, + ), + ), + ), + ), + ], + ), + ); + } +} diff --git a/lib/onboarding/components/on_boarding_item_page.dart b/lib/onboarding/components/on_boarding_item_page.dart new file mode 100644 index 0000000..5179f66 --- /dev/null +++ b/lib/onboarding/components/on_boarding_item_page.dart @@ -0,0 +1,99 @@ +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/material.dart'; +import 'package:smooth_page_indicator/smooth_page_indicator.dart'; + +import '../../generated/assets.gen.dart'; +import '../../generated/colors.gen.dart'; +import '../../generated/locale_keys.g.dart'; + +class OnBoardingItemPage extends StatelessWidget { + const OnBoardingItemPage({ + super.key, + required PageController controller, + required String title, + required this.context, + }) : _controller = controller, + _title = title; + + final PageController _controller; + final String _title; + final BuildContext context; + + @override + Widget build(BuildContext context) { + return SizedBox.expand( + child: Column( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + IgnorePointer( + ignoring: true, + child: Image.asset( + Assets.images.marvelLogo.path, + width: 185.0, + height: 75.0, + fit: BoxFit.cover, + alignment: Alignment.center, + ), + ), + const SizedBox(height: 60.0), + IgnorePointer( + ignoring: true, + child: SmoothPageIndicator( + controller: _controller, + count: 6, + effect: const WormEffect( + activeDotColor: ColorName.crimsonApprox, + dotColor: Colors.white, + dotHeight: 10.0, + dotWidth: 10.0, + spacing: 10.0, + ), + ), + ), + const SizedBox(height: 40.0), + IgnorePointer( + ignoring: true, + child: SizedBox( + height: 115.0, + child: Text( + _title, + style: Theme.of(context).textTheme.titleLarge, + textAlign: TextAlign.center, + ), + ), + ), + const SizedBox(height: 53.0), + Padding( + padding: const EdgeInsets.only( + left: 50.0, + right: 50.0, + ), + child: SizedBox( + width: double.infinity, + height: 50.0, + child: TextButton( + style: TextButton.styleFrom( + backgroundColor: ColorName.crimsonApprox, + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.all(Radius.zero), + ), + ), + onPressed: () { + _controller.nextPage( + duration: const Duration(milliseconds: 250), + curve: Curves.easeIn, + ); + }, + child: Text( + LocaleKeys.continueOnBoarding.tr(), + style: Theme.of(context).textTheme.titleMedium, + ), + ), + ), + ), + const SizedBox(height: 40.0), + ], + ), + ); + } +} diff --git a/lib/onboarding/on_boarding.dart b/lib/onboarding/on_boarding.dart new file mode 100644 index 0000000..8bbde5a --- /dev/null +++ b/lib/onboarding/on_boarding.dart @@ -0,0 +1,93 @@ +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_movie_clean/generated/assets.gen.dart'; +import 'package:flutter_movie_clean/generated/locale_keys.g.dart'; + +import 'components/on_boarding_end_page.dart'; +import 'components/on_boarding_item_page.dart'; + +class OnBoardingScreen extends StatefulWidget { + const OnBoardingScreen({Key? key}) : super(key: key); + + @override + State createState() => _OnBoardingScreenState(); +} + +class _OnBoardingScreenState extends State { + final PageController _controller = PageController(); + bool _isEndPage = false; + final List _titles = [ + LocaleKeys.onBoardingPageFirstTitle.tr(), + LocaleKeys.onBoardingPageSecondTitle.tr(), + LocaleKeys.onBoardingPageThirdTitle.tr(), + LocaleKeys.onBoardingPageFourthTitle.tr(), + LocaleKeys.onBoardingPageFifthTitle.tr(), + ]; + String _title = ''; + final List _posterPaths = [ + Assets.images.onboardings.poster1.path, + Assets.images.onboardings.poster2.path, + Assets.images.onboardings.poster3.path, + Assets.images.onboardings.poster4.path, + Assets.images.onboardings.poster5.path, + Assets.images.onboardings.poster6.path, + ]; + + final Gradient _maskingGradient = const LinearGradient( + colors: [Colors.transparent, Colors.black], + stops: [0.0, 0.8], + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + ); + + @override + void initState() { + super.initState(); + _title = _titles.first; + } + + @override + Widget build(BuildContext context) { + return Scaffold( + body: Stack( + children: [ + ShaderMask( + shaderCallback: (bounds) => _maskingGradient.createShader(bounds), + blendMode: BlendMode.darken, + child: PageView( + controller: _controller, + onPageChanged: (index) { + setState(() { + _isEndPage = index == _titles.length ? true : false; + _title = index < _titles.length ? _titles.elementAt(index) : ''; + }); + }, + children: _posterPaths.map((path) => _posterItemPageView(path)).toList(), + ), + ), + _isEndPage + ? OnBoardingEndPage(controller: _controller, context: context) + : OnBoardingItemPage(controller: _controller, title: _title, context: context) + ], + ), + ); + } + + Widget _posterItemPageView(String poster) { + return Column( + children: [ + Expanded( + child: Image.asset( + width: double.infinity, + poster, + fit: BoxFit.fill, + ), + ), + Container( + height: 150.0, + color: Colors.black, + ) + ], + ); + } +} diff --git a/lib/router/router.dart b/lib/router/router.dart new file mode 100644 index 0000000..63dd3f8 --- /dev/null +++ b/lib/router/router.dart @@ -0,0 +1,90 @@ +import 'package:flutter/material.dart'; +import 'package:go_router/go_router.dart'; + +import '../exports/screens.dart'; +import '../widgets/scaffold_with_nav_bar.dart'; + +final GlobalKey _rootNavigatorKey = GlobalKey(debugLabel: 'root'); +final GlobalKey _shellNavigatorKey = GlobalKey(debugLabel: 'shell'); +const ValueKey _scaffoldKey = ValueKey('App scaffold'); + +final GoRouter router = GoRouter( + navigatorKey: _rootNavigatorKey, + initialLocation: '/', + routes: [ + GoRoute( + name: 'on_boarding', + path: '/', + builder: (BuildContext context, GoRouterState state) => const OnBoardingScreen(), + ), + ShellRoute( + navigatorKey: _shellNavigatorKey, + builder: (BuildContext context, GoRouterState state, Widget child) { + return ScaffoldWithNavBar(child: child); + }, + routes: [ + GoRoute( + name: 'home', + path: '/home', + pageBuilder: (BuildContext context, GoRouterState state) { + return FadeTransitionPage( + key: _scaffoldKey, + child: const HomeScreen(), + ); + }, + routes: [ + GoRoute( + name: 'movie_details', + path: 'movie_details', + parentNavigatorKey: _rootNavigatorKey, + builder: (BuildContext context, GoRouterState state) => const MovieDetailsScreen(), + ), + ]), + GoRoute( + name: 'categories', + path: '/categories', + pageBuilder: (BuildContext context, GoRouterState state) { + return FadeTransitionPage( + key: _scaffoldKey, + child: const CategoriesScreen(), + ); + }), + GoRoute( + name: 'downloads', + path: '/downloads', + pageBuilder: (BuildContext context, GoRouterState state) { + return FadeTransitionPage( + key: _scaffoldKey, + child: const DownloadsScreen(), + ); + }), + GoRoute( + name: 'more', + path: '/more', + pageBuilder: (BuildContext context, GoRouterState state) { + return FadeTransitionPage( + key: _scaffoldKey, + child: const MoreScreen(), + ); + }), + ], + ), + ], +); + +class FadeTransitionPage extends CustomTransitionPage { + FadeTransitionPage({ + required LocalKey super.key, + required super.child, + }) : super(transitionsBuilder: ( + BuildContext context, + Animation animation, + Animation secondaryAnimation, + Widget child, + ) { + return FadeTransition( + opacity: animation.drive(CurveTween(curve: Curves.easeIn)), + child: child, + ); + }); +} diff --git a/lib/theme/app_theme.dart b/lib/theme/app_theme.dart new file mode 100644 index 0000000..2bec9ef --- /dev/null +++ b/lib/theme/app_theme.dart @@ -0,0 +1,53 @@ +import 'package:flutter/material.dart'; + +class AppTheme { + static TextTheme lightTextTheme = const TextTheme( + titleLarge: TextStyle( + fontSize: 24.0, + fontWeight: FontWeight.w600, + color: Colors.black, + ), + titleMedium: TextStyle( + fontSize: 20.0, + fontWeight: FontWeight.w500, + color: Colors.black, + ), + titleSmall: TextStyle( + fontSize: 12.0, + fontWeight: FontWeight.w600, + color: Colors.black, + ), + ); + + static TextTheme dartTextTheme = const TextTheme( + titleLarge: TextStyle( + fontSize: 24.0, + fontWeight: FontWeight.w600, + color: Colors.white, + ), + titleMedium: TextStyle( + fontSize: 20.0, + fontWeight: FontWeight.w500, + color: Colors.white, + ), + titleSmall: TextStyle( + fontSize: 12.0, + fontWeight: FontWeight.w600, + color: Colors.white, + ), + ); + + static ThemeData light() { + return ThemeData( + brightness: Brightness.light, + textTheme: lightTextTheme, + ); + } + + static ThemeData dark() { + return ThemeData( + brightness: Brightness.dark, + textTheme: dartTextTheme, + ); + } +} diff --git a/lib/widgets/header_widget.dart b/lib/widgets/header_widget.dart new file mode 100644 index 0000000..795922f --- /dev/null +++ b/lib/widgets/header_widget.dart @@ -0,0 +1,52 @@ +import 'package:flutter/material.dart'; + +import '../generated/assets.gen.dart'; + +class HeaderWidget extends StatelessWidget { + const HeaderWidget({Key? key, required this.onProfileTapped}) : super(key: key); + + final Function() onProfileTapped; + + @override + Widget build(BuildContext context) { + return Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const Expanded(child: SizedBox()), + Expanded( + flex: 1, + child: Align( + alignment: Alignment.center, + child: Image.asset( + Assets.images.marvelLogo.path, + width: 100.0, + height: 45.26, + ), + ), + ), + Expanded( + flex: 1, + child: Align( + alignment: Alignment.centerRight, + child: Padding( + padding: const EdgeInsets.only(right: 16.0, top: 16.0, bottom: 16.0), + child: GestureDetector( + child: CircleAvatar( + radius: 35.0, + backgroundImage: AssetImage(Assets.images.bgProfile.path), + child: CircleAvatar( + radius: 27.0, + backgroundImage: AssetImage(Assets.images.profileAvatar.path), + ), + ), + onTap: () { + onProfileTapped(); + }, + ), + ), + ), + ), + ], + ); + } +} diff --git a/lib/widgets/scaffold_with_nav_bar.dart b/lib/widgets/scaffold_with_nav_bar.dart new file mode 100644 index 0000000..48bf5ef --- /dev/null +++ b/lib/widgets/scaffold_with_nav_bar.dart @@ -0,0 +1,98 @@ +import 'dart:ui'; + +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/material.dart'; +import 'package:go_router/go_router.dart'; + +import '../generated/assets.gen.dart'; +import '../generated/colors.gen.dart'; +import '../generated/locale_keys.g.dart'; + +class ScaffoldWithNavBar extends StatelessWidget { + final Widget child; + + const ScaffoldWithNavBar({required this.child, super.key}); + + @override + Widget build(BuildContext context) { + return Scaffold( + extendBody: true, + body: child, + backgroundColor: Colors.black, + bottomNavigationBar: ClipRRect( + child: BackdropFilter( + filter: ImageFilter.blur(sigmaX: 10, sigmaY: 10), + child: BottomNavigationBar( + items: [ + BottomNavigationBarItem( + icon: Image.asset(Assets.images.icons.icHome.path), + activeIcon: + Image.asset(Assets.images.icons.icHome.path, color: ColorName.crimsonApprox), + label: LocaleKeys.home.tr(), + ), + BottomNavigationBarItem( + icon: Image.asset(Assets.images.icons.icCategories.path), + activeIcon: Image.asset(Assets.images.icons.icCategories.path, + color: ColorName.crimsonApprox), + label: LocaleKeys.categories.tr(), + ), + BottomNavigationBarItem( + icon: Image.asset(Assets.images.icons.icDownloads.path), + activeIcon: Image.asset(Assets.images.icons.icDownloads.path, + color: ColorName.crimsonApprox), + label: LocaleKeys.downloads.tr(), + ), + BottomNavigationBarItem( + icon: Image.asset(Assets.images.icons.icMore.path), + activeIcon: + Image.asset(Assets.images.icons.icMore.path, color: ColorName.crimsonApprox), + label: LocaleKeys.more.tr(), + ) + ], + elevation: 0, + type: BottomNavigationBarType.fixed, + unselectedItemColor: Colors.white, + selectedItemColor: ColorName.crimsonApprox, + backgroundColor: Colors.black.withOpacity(0.7), + currentIndex: _calculateSelectedIndex(context), + onTap: (int index) => _onItemTapped(index, context), + ), + ), + ), + ); + } + + static int _calculateSelectedIndex(BuildContext context) { + final String location = GoRouterState.of(context).location; + if (location.startsWith('/home')) { + return 0; + } + if (location.startsWith('/categories')) { + return 1; + } + if (location.startsWith('/downloads')) { + return 2; + } + if (location.startsWith('/more')) { + return 3; + } + return 0; + } + + _onItemTapped(int index, BuildContext context) { + switch (index) { + case 0: + GoRouter.of(context).go('/home'); + break; + case 1: + GoRouter.of(context).go('/categories'); + break; + case 2: + GoRouter.of(context).go('/downloads'); + break; + case 3: + GoRouter.of(context).go('/more'); + break; + } + } +} diff --git a/pubspec.lock b/pubspec.lock index c317f07..0582a28 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1,6 +1,30 @@ # Generated by pub # See https://dart.dev/tools/pub/glossary#lockfile packages: + _fe_analyzer_shared: + dependency: transitive + description: + name: _fe_analyzer_shared + sha256: "98d1d33ed129b372846e862de23a0fc365745f4d7b5e786ce667fcbbb7ac5c07" + url: "https://pub.dev" + source: hosted + version: "55.0.0" + analyzer: + dependency: transitive + description: + name: analyzer + sha256: "881348aed9b0b425882c97732629a6a31093c8ff20fc4b3b03fb9d3d50a3a126" + url: "https://pub.dev" + source: hosted + version: "5.7.1" + args: + dependency: transitive + description: + name: args + sha256: "4cab82a83ffef80b262ddedf47a0a8e56ee6fbf7fe21e6e768b02792034dd440" + url: "https://pub.dev" + source: hosted + version: "2.4.0" async: dependency: transitive description: @@ -9,6 +33,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.10.0" + bloc: + dependency: transitive + description: + name: bloc + sha256: "658a5ae59edcf1e58aac98b000a71c762ad8f46f1394c34a52050cafb3e11a80" + url: "https://pub.dev" + source: hosted + version: "8.1.1" boolean_selector: dependency: transitive description: @@ -17,6 +49,78 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.1" + build: + dependency: transitive + description: + name: build + sha256: "3fbda25365741f8251b39f3917fb3c8e286a96fd068a5a242e11c2012d495777" + url: "https://pub.dev" + source: hosted + version: "2.3.1" + build_config: + dependency: transitive + description: + name: build_config + sha256: bf80fcfb46a29945b423bd9aad884590fb1dc69b330a4d4700cac476af1708d1 + url: "https://pub.dev" + source: hosted + version: "1.1.1" + build_daemon: + dependency: transitive + description: + name: build_daemon + sha256: "757153e5d9cd88253cb13f28c2fb55a537dc31fefd98137549895b5beb7c6169" + url: "https://pub.dev" + source: hosted + version: "3.1.1" + build_resolvers: + dependency: transitive + description: + name: build_resolvers + sha256: db49b8609ef8c81cca2b310618c3017c00f03a92af44c04d310b907b2d692d95 + url: "https://pub.dev" + source: hosted + version: "2.2.0" + build_runner: + dependency: "direct dev" + description: + name: build_runner + sha256: b0a8a7b8a76c493e85f1b84bffa0588859a06197863dba8c9036b15581fd9727 + url: "https://pub.dev" + source: hosted + version: "2.3.3" + build_runner_core: + dependency: transitive + description: + name: build_runner_core + sha256: "14febe0f5bac5ae474117a36099b4de6f1dbc52df6c5e55534b3da9591bf4292" + url: "https://pub.dev" + source: hosted + version: "7.2.7" + 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: "31b7c748fd4b9adf8d25d72a4c4a59ef119f12876cf414f94f8af5131d5fa2b0" + url: "https://pub.dev" + source: hosted + version: "8.4.4" + carousel_slider: + dependency: "direct main" + description: + name: carousel_slider + sha256: "9c695cc963bf1d04a47bd6021f68befce8970bcd61d24938e1fb0918cf5d9c42" + url: "https://pub.dev" + source: hosted + version: "4.2.1" characters: dependency: transitive description: @@ -25,6 +129,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.2.1" + checked_yaml: + dependency: transitive + description: + name: checked_yaml + sha256: "3d1505d91afa809d177efd4eed5bb0eb65805097a1463abdd2add076effae311" + url: "https://pub.dev" + source: hosted + version: "2.0.2" clock: dependency: transitive description: @@ -33,6 +145,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.1.1" + code_builder: + dependency: transitive + description: + name: code_builder + sha256: "0d43dd1288fd145de1ecc9a3948ad4a6d5a82f0a14c4fdd0892260787d975cbe" + url: "https://pub.dev" + source: hosted + version: "4.4.0" collection: dependency: transitive description: @@ -41,6 +161,30 @@ packages: url: "https://pub.dev" source: hosted version: "1.17.0" + color: + dependency: transitive + description: + name: color + sha256: ddcdf1b3badd7008233f5acffaf20ca9f5dc2cd0172b75f68f24526a5f5725cb + url: "https://pub.dev" + source: hosted + version: "3.0.0" + convert: + dependency: transitive + description: + name: convert + sha256: "0f08b14755d163f6e2134cb58222dd25ea2a2ee8a195e53983d57c075324d592" + url: "https://pub.dev" + source: hosted + version: "3.1.1" + crypto: + dependency: transitive + description: + name: crypto + sha256: aa274aa7774f8964e4f4f38cc994db7b6158dd36e9187aaceaddc994b35c6c67 + url: "https://pub.dev" + source: hosted + version: "3.0.2" cupertino_icons: dependency: "direct main" description: @@ -49,6 +193,22 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.5" + dart_style: + dependency: transitive + description: + name: dart_style + sha256: "6d691edde054969f0e0f26abb1b30834b5138b963793e56f69d3a9a4435e6352" + url: "https://pub.dev" + source: hosted + version: "2.3.0" + dartx: + dependency: transitive + description: + name: dartx + sha256: "45d7176701f16c5a5e00a4798791c1964bc231491b879369c818dd9a9c764871" + url: "https://pub.dev" + source: hosted + version: "1.1.0" data: dependency: "direct main" description: @@ -63,6 +223,22 @@ packages: relative: true source: path version: "0.0.1" + easy_localization: + dependency: "direct main" + description: + name: easy_localization + sha256: "6a2e99fa0bfe5765bf4c6ca9b137d5de2c75593007178c5e4cd2ae985f870080" + url: "https://pub.dev" + source: hosted + version: "3.0.1" + easy_logger: + dependency: transitive + description: + name: easy_logger + sha256: c764a6e024846f33405a2342caf91c62e357c24b02c04dbc712ef232bf30ffb7 + url: "https://pub.dev" + source: hosted + version: "0.0.2" fake_async: dependency: transitive description: @@ -71,11 +247,59 @@ packages: url: "https://pub.dev" source: hosted version: "1.3.1" + ffi: + dependency: transitive + description: + name: ffi + sha256: a38574032c5f1dd06c4aee541789906c12ccaab8ba01446e800d9c5b79c4a978 + url: "https://pub.dev" + source: hosted + version: "2.0.1" + file: + dependency: transitive + description: + name: file + sha256: "1b92bec4fc2a72f59a8e15af5f52cd441e4a7860b49499d69dfa817af20e925d" + url: "https://pub.dev" + source: hosted + version: "6.1.4" + fixnum: + dependency: transitive + description: + name: fixnum + sha256: "25517a4deb0c03aa0f32fd12db525856438902d9c16536311e76cdc57b31d7d1" + url: "https://pub.dev" + source: hosted + version: "1.1.0" flutter: dependency: "direct main" description: flutter source: sdk version: "0.0.0" + flutter_bloc: + dependency: "direct main" + description: + name: flutter_bloc + sha256: "434951eea948dbe87f737b674281465f610b8259c16c097b8163ce138749a775" + url: "https://pub.dev" + source: hosted + version: "8.1.2" + flutter_gen_core: + dependency: transitive + description: + name: flutter_gen_core + sha256: e74db9fc706ce43ef0dfd4b296fcfa10f84c4d862b9b68a087e7c703f97c7a0a + url: "https://pub.dev" + source: hosted + version: "5.2.0" + flutter_gen_runner: + dependency: "direct dev" + description: + name: flutter_gen_runner + sha256: "434511d7c3f7bb5c67d89a16451056093953bebf7afa8336baeceddfc6fe2a21" + url: "https://pub.dev" + source: hosted + version: "5.2.0" flutter_lints: dependency: "direct dev" description: @@ -84,11 +308,85 @@ packages: url: "https://pub.dev" source: hosted version: "2.0.1" + flutter_localizations: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" flutter_test: dependency: "direct dev" description: flutter source: sdk version: "0.0.0" + flutter_web_plugins: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" + frontend_server_client: + dependency: transitive + description: + name: frontend_server_client + sha256: "408e3ca148b31c20282ad6f37ebfa6f4bdc8fede5b74bc2f08d9d92b55db3612" + url: "https://pub.dev" + source: hosted + version: "3.2.0" + glob: + dependency: transitive + description: + name: glob + sha256: "4515b5b6ddb505ebdd242a5f2cc5d22d3d6a80013789debfbda7777f47ea308c" + url: "https://pub.dev" + source: hosted + version: "2.1.1" + go_router: + dependency: "direct main" + description: + name: go_router + sha256: "871f6f89426ab20286d4e8aaa0f85b6fe91607e1720375f1baecbd34602df409" + url: "https://pub.dev" + source: hosted + version: "6.3.0" + graphs: + dependency: transitive + description: + name: graphs + sha256: f9e130f3259f52d26f0cfc0e964513796dafed572fa52e45d2f8d6ca14db39b2 + url: "https://pub.dev" + source: hosted + version: "2.2.0" + http_multi_server: + dependency: transitive + description: + name: http_multi_server + sha256: "97486f20f9c2f7be8f514851703d0119c3596d14ea63227af6f7a481ef2b2f8b" + url: "https://pub.dev" + source: hosted + version: "3.2.1" + http_parser: + dependency: transitive + description: + name: http_parser + sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b" + url: "https://pub.dev" + source: hosted + version: "4.0.2" + intl: + dependency: transitive + description: + name: intl + sha256: "910f85bce16fb5c6f614e117efa303e85a1731bb0081edf3604a2ae6e9a3cc91" + url: "https://pub.dev" + source: hosted + version: "0.17.0" + io: + dependency: transitive + description: + name: io + sha256: "2ec25704aba361659e10e3e5f5d672068d332fc8ac516421d483a11e5cbd061e" + url: "https://pub.dev" + source: hosted + version: "1.0.4" js: dependency: transitive description: @@ -97,6 +395,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.6.5" + json_annotation: + dependency: transitive + description: + name: json_annotation + sha256: c33da08e136c3df0190bd5bbe51ae1df4a7d96e7954d1d7249fea2968a72d317 + url: "https://pub.dev" + source: hosted + version: "4.8.0" lints: dependency: transitive description: @@ -105,6 +411,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.0.1" + logging: + dependency: transitive + description: + name: logging + sha256: "04094f2eb032cbb06c6f6e8d3607edcfcb0455e2bb6cbc010cb01171dcb64e6d" + url: "https://pub.dev" + source: hosted + version: "1.1.1" matcher: dependency: transitive description: @@ -129,6 +443,30 @@ packages: url: "https://pub.dev" source: hosted version: "1.8.0" + mime: + dependency: transitive + description: + name: mime + sha256: e4ff8e8564c03f255408decd16e7899da1733852a9110a58fe6d1b817684a63e + url: "https://pub.dev" + source: hosted + version: "1.0.4" + nested: + dependency: transitive + description: + name: nested + sha256: "03bac4c528c64c95c722ec99280375a6f2fc708eec17c7b3f07253b626cd2a20" + url: "https://pub.dev" + source: hosted + version: "1.0.0" + package_config: + dependency: transitive + description: + name: package_config + sha256: "1c5b77ccc91e4823a5af61ee74e6b972db1ef98c2ff5a18d3161c982a55448bd" + url: "https://pub.dev" + source: hosted + version: "2.1.0" path: dependency: transitive description: @@ -137,11 +475,179 @@ packages: url: "https://pub.dev" source: hosted version: "1.8.2" + path_provider_linux: + dependency: transitive + description: + name: path_provider_linux + sha256: "2ae08f2216225427e64ad224a24354221c2c7907e448e6e0e8b57b1eb9f10ad1" + url: "https://pub.dev" + source: hosted + version: "2.1.10" + path_provider_platform_interface: + dependency: transitive + description: + name: path_provider_platform_interface + sha256: "57585299a729335f1298b43245842678cb9f43a6310351b18fb577d6e33165ec" + url: "https://pub.dev" + source: hosted + version: "2.0.6" + path_provider_windows: + dependency: transitive + description: + name: path_provider_windows + sha256: f53720498d5a543f9607db4b0e997c4b5438884de25b0f73098cc2671a51b130 + url: "https://pub.dev" + source: hosted + version: "2.1.5" + petitparser: + dependency: transitive + description: + name: petitparser + sha256: "49392a45ced973e8d94a85fdb21293fbb40ba805fc49f2965101ae748a3683b4" + url: "https://pub.dev" + source: hosted + version: "5.1.0" + platform: + dependency: transitive + description: + name: platform + sha256: "4a451831508d7d6ca779f7ac6e212b4023dd5a7d08a27a63da33756410e32b76" + url: "https://pub.dev" + source: hosted + version: "3.1.0" + plugin_platform_interface: + dependency: transitive + description: + name: plugin_platform_interface + sha256: "6a2128648c854906c53fa8e33986fc0247a1116122f9534dd20e3ab9e16a32bc" + url: "https://pub.dev" + source: hosted + version: "2.1.4" + pool: + dependency: transitive + description: + name: pool + sha256: "20fe868b6314b322ea036ba325e6fc0711a22948856475e2c2b6306e8ab39c2a" + url: "https://pub.dev" + source: hosted + version: "1.5.1" + process: + dependency: transitive + description: + name: process + sha256: "53fd8db9cec1d37b0574e12f07520d582019cb6c44abf5479a01505099a34a09" + url: "https://pub.dev" + source: hosted + version: "4.2.4" + provider: + dependency: transitive + description: + name: provider + sha256: cdbe7530b12ecd9eb455bdaa2fcb8d4dad22e80b8afb4798b41479d5ce26847f + url: "https://pub.dev" + source: hosted + version: "6.0.5" + pub_semver: + dependency: transitive + description: + name: pub_semver + sha256: "307de764d305289ff24ad257ad5c5793ce56d04947599ad68b3baa124105fc17" + url: "https://pub.dev" + source: hosted + version: "2.1.3" + pubspec_parse: + dependency: transitive + description: + name: pubspec_parse + sha256: ec85d7d55339d85f44ec2b682a82fea340071e8978257e5a43e69f79e98ef50c + url: "https://pub.dev" + source: hosted + version: "1.2.2" + shared_preferences: + dependency: transitive + description: + name: shared_preferences + sha256: ee6257848f822b8481691f20c3e6d2bfee2e9eccb2a3d249907fcfb198c55b41 + url: "https://pub.dev" + source: hosted + version: "2.0.18" + shared_preferences_android: + dependency: transitive + description: + name: shared_preferences_android + sha256: ad423a80fe7b4e48b50d6111b3ea1027af0e959e49d485712e134863d9c1c521 + url: "https://pub.dev" + source: hosted + version: "2.0.17" + shared_preferences_foundation: + dependency: transitive + description: + name: shared_preferences_foundation + sha256: "1e755f8583229f185cfca61b1d80fb2344c9d660e1c69ede5450d8f478fa5310" + url: "https://pub.dev" + source: hosted + version: "2.1.5" + shared_preferences_linux: + dependency: transitive + description: + name: shared_preferences_linux + sha256: "3a59ed10890a8409ad0faad7bb2957dab4b92b8fbe553257b05d30ed8af2c707" + url: "https://pub.dev" + source: hosted + version: "2.1.5" + shared_preferences_platform_interface: + dependency: transitive + description: + name: shared_preferences_platform_interface + sha256: "824bfd02713e37603b2bdade0842e47d56e7db32b1dcdd1cae533fb88e2913fc" + url: "https://pub.dev" + source: hosted + version: "2.1.1" + shared_preferences_web: + dependency: transitive + description: + name: shared_preferences_web + sha256: "0dc2633f215a3d4aa3184c9b2c5766f4711e4e5a6b256e62aafee41f89f1bfb8" + url: "https://pub.dev" + source: hosted + version: "2.0.6" + shared_preferences_windows: + dependency: transitive + description: + name: shared_preferences_windows + sha256: "71bcd669bb9cdb6b39f22c4a7728b6d49e934f6cba73157ffa5a54f1eed67436" + url: "https://pub.dev" + source: hosted + version: "2.1.5" + shelf: + dependency: transitive + description: + name: shelf + sha256: c24a96135a2ccd62c64b69315a14adc5c3419df63b4d7c05832a346fdb73682c + url: "https://pub.dev" + source: hosted + version: "1.4.0" + shelf_web_socket: + dependency: transitive + description: + name: shelf_web_socket + sha256: a988c0e8d8ffbdb8a28aa7ec8e449c260f3deb808781fe1284d22c5bba7156e8 + url: "https://pub.dev" + source: hosted + version: "1.0.3" sky_engine: dependency: transitive description: flutter source: sdk version: "0.0.99" + smooth_page_indicator: + dependency: "direct main" + description: + name: smooth_page_indicator + sha256: "8c301bc686892306cd41672c1880167f140c16be305d5ede8201fefd9fcda829" + url: "https://pub.dev" + source: hosted + version: "1.0.1" source_span: dependency: transitive description: @@ -166,6 +672,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.1" + stream_transform: + dependency: transitive + description: + name: stream_transform + sha256: "14a00e794c7c11aa145a170587321aedce29769c08d7f58b1d141da75e3b1c6f" + url: "https://pub.dev" + source: hosted + version: "2.1.0" string_scanner: dependency: transitive description: @@ -190,6 +704,30 @@ packages: url: "https://pub.dev" source: hosted version: "0.4.16" + time: + dependency: transitive + description: + name: time + sha256: "83427e11d9072e038364a5e4da559e85869b227cf699a541be0da74f14140124" + url: "https://pub.dev" + source: hosted + version: "2.1.3" + timing: + dependency: transitive + description: + name: timing + sha256: "70a3b636575d4163c477e6de42f247a23b315ae20e86442bebe32d3cabf61c32" + url: "https://pub.dev" + source: hosted + version: "1.0.1" + typed_data: + dependency: transitive + description: + name: typed_data + sha256: "26f87ade979c47a150c9eaab93ccd2bebe70a27dc0b4b29517f2904f04eb11a5" + url: "https://pub.dev" + source: hosted + version: "1.3.1" vector_math: dependency: transitive description: @@ -198,6 +736,54 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.4" + watcher: + dependency: transitive + description: + name: watcher + sha256: "6a7f46926b01ce81bfc339da6a7f20afbe7733eff9846f6d6a5466aa4c6667c0" + url: "https://pub.dev" + source: hosted + version: "1.0.2" + web_socket_channel: + dependency: transitive + description: + name: web_socket_channel + sha256: ca49c0bc209c687b887f30527fb6a9d80040b072cc2990f34b9bec3e7663101b + url: "https://pub.dev" + source: hosted + version: "2.3.0" + win32: + dependency: transitive + description: + name: win32 + sha256: c9ebe7ee4ab0c2194e65d3a07d8c54c5d00bb001b76081c4a04cdb8448b59e46 + url: "https://pub.dev" + source: hosted + version: "3.1.3" + xdg_directories: + dependency: transitive + description: + name: xdg_directories + sha256: ee1505df1426458f7f60aac270645098d318a8b4766d85fde75f76f2e21807d1 + url: "https://pub.dev" + source: hosted + version: "1.0.0" + xml: + dependency: transitive + description: + name: xml + sha256: "979ee37d622dec6365e2efa4d906c37470995871fe9ae080d967e192d88286b5" + url: "https://pub.dev" + source: hosted + version: "6.2.2" + yaml: + dependency: transitive + description: + name: yaml + sha256: "23812a9b125b48d4007117254bca50abb6c712352927eece9e155207b1db2370" + url: "https://pub.dev" + source: hosted + version: "3.1.1" sdks: dart: ">=2.19.4 <3.0.0" - flutter: ">=1.17.0" + flutter: ">=3.3.0" diff --git a/pubspec.yaml b/pubspec.yaml index 7317433..c4f1122 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -38,8 +38,15 @@ dependencies: # The following adds the Cupertino Icons font to your application. # Use with the CupertinoIcons class for iOS style icons. cupertino_icons: ^1.0.5 + easy_localization: ^3.0.1 + smooth_page_indicator: ^1.0.1 + flutter_bloc: ^8.1.2 + go_router: ^6.3.0 + carousel_slider: ^4.2.1 dev_dependencies: + build_runner: + flutter_gen_runner: flutter_test: sdk: flutter @@ -50,11 +57,27 @@ dev_dependencies: # rules and activating additional ones. flutter_lints: ^2.0.1 +flutter_gen: + output: lib/generated # Optional (default: lib/gen/) + line_length: 160 # Optional (default: 80) + null_safety: true + + colors: + inputs: + - assets/color/colors.xml # 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: + assets: + - assets/images/ + - assets/images/icons/ + - assets/images/onboardings/ + - assets/images/movies/ + - assets/images/series/ + - assets/images/trendings/ + - assets/translations/ # The following line ensures that the Material Icons font is # included with your application, so that you can use the icons in diff --git a/test/widget_test.dart b/test/widget_test.dart index 60e5acb..84aae55 100644 --- a/test/widget_test.dart +++ b/test/widget_test.dart @@ -13,7 +13,7 @@ import 'package:flutter_movie_clean/main.dart'; void main() { testWidgets('Counter increments smoke test', (WidgetTester tester) async { // Build our app and trigger a frame. - await tester.pumpWidget(const MyApp()); + await tester.pumpWidget(MyApp()); // Verify that our counter starts at 0. expect(find.text('0'), findsOneWidget);