diff --git a/README.md b/README.md index 61a0736..ef731b3 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# hw_3 +# hw_4 A new Flutter project. diff --git a/android/.gradle/6.7/executionHistory/executionHistory.bin b/android/.gradle/6.7/executionHistory/executionHistory.bin index d477a92..99df4b1 100644 Binary files a/android/.gradle/6.7/executionHistory/executionHistory.bin and b/android/.gradle/6.7/executionHistory/executionHistory.bin differ diff --git a/android/.gradle/6.7/executionHistory/executionHistory.lock b/android/.gradle/6.7/executionHistory/executionHistory.lock index 651df7f..1dc2f68 100644 Binary files a/android/.gradle/6.7/executionHistory/executionHistory.lock and b/android/.gradle/6.7/executionHistory/executionHistory.lock differ diff --git a/android/.gradle/6.7/fileHashes/fileHashes.bin b/android/.gradle/6.7/fileHashes/fileHashes.bin index 98c7346..1765c3e 100644 Binary files a/android/.gradle/6.7/fileHashes/fileHashes.bin and b/android/.gradle/6.7/fileHashes/fileHashes.bin differ diff --git a/android/.gradle/6.7/fileHashes/fileHashes.lock b/android/.gradle/6.7/fileHashes/fileHashes.lock index 191508c..364c6f6 100644 Binary files a/android/.gradle/6.7/fileHashes/fileHashes.lock and b/android/.gradle/6.7/fileHashes/fileHashes.lock differ diff --git a/android/.gradle/buildOutputCleanup/buildOutputCleanup.lock b/android/.gradle/buildOutputCleanup/buildOutputCleanup.lock index 734c969..8bab98b 100644 Binary files a/android/.gradle/buildOutputCleanup/buildOutputCleanup.lock and b/android/.gradle/buildOutputCleanup/buildOutputCleanup.lock differ diff --git a/lib/data/data.dart b/lib/data/data.dart index 1935fad..4940580 100644 --- a/lib/data/data.dart +++ b/lib/data/data.dart @@ -57,7 +57,7 @@ class VideoComments { Channel currentUser = Channel( 'John Steck', logoImagePath: 'assets/images/profile_screen/avatars/profile.png', - imageUrl: 'https://avatars.githubusercontent.com/u/63707307?v=4', + imageUrl: 'https://avatars.githubusercontent.com/u/63707307', subscribersCounter: 100000, ); diff --git a/lib/globalStateManagement/themeManagement.dart b/lib/globalStateManagement/themeManagement.dart new file mode 100644 index 0000000..c8fd24a --- /dev/null +++ b/lib/globalStateManagement/themeManagement.dart @@ -0,0 +1,14 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'themes.dart'; + +class ThemeManagement with ChangeNotifier { + ThemeData _currentTheme = mainTheme; + + ThemeData get currentTheme => _currentTheme; + + void toggleTheme() { + _currentTheme = _currentTheme == mainTheme ? secondTheme : mainTheme; + notifyListeners(); + } +} diff --git a/lib/globalStateManagement/themes.dart b/lib/globalStateManagement/themes.dart new file mode 100644 index 0000000..994b5e1 --- /dev/null +++ b/lib/globalStateManagement/themes.dart @@ -0,0 +1,15 @@ +import 'package:flutter/material.dart'; + +ThemeData mainTheme = ThemeData( + brightness: Brightness.dark, + bottomNavigationBarTheme: + const BottomNavigationBarThemeData(selectedItemColor: Colors.white), +); + +ThemeData secondTheme = ThemeData( + + brightness: Brightness.light, + bottomNavigationBarTheme: + const BottomNavigationBarThemeData(selectedItemColor: Colors.black), + +); diff --git a/lib/globalStateManagement/userImageManagement.dart b/lib/globalStateManagement/userImageManagement.dart new file mode 100644 index 0000000..27caf7e --- /dev/null +++ b/lib/globalStateManagement/userImageManagement.dart @@ -0,0 +1,19 @@ +import 'dart:math'; + +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:hw_3/data/data.dart'; + +class ImageManagement with ChangeNotifier { + String _randomImage = currentUser.imageUrl; + String get randomImage => _randomImage; + + void generateRandomImage() { + var random = new Random(); + int min = 1; + int max = 63707307; + int result = min + random.nextInt(max - min); + _randomImage = 'https://avatars.githubusercontent.com/u/$result'; + notifyListeners(); + } +} diff --git a/lib/main.dart b/lib/main.dart index e09eafc..5b5adae 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,10 +1,17 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; +import 'package:hw_3/globalStateManagement/themeManagement.dart'; import 'package:hw_3/screens/nav_screen.dart'; +import 'package:provider/provider.dart'; + +import 'globalStateManagement/userImageManagement.dart'; void main() { - runApp(MyApp()); + runApp(MultiProvider(providers: [ + ChangeNotifierProvider(create: (_) => ImageManagement()), + ChangeNotifierProvider(create: (_) => ThemeManagement()), + ], child: MyApp())); } class MyApp extends StatelessWidget { @@ -12,15 +19,12 @@ class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { + // var incrementValue = context.watch().count; + return MaterialApp( - title: 'Flutter YouTube UI', - debugShowCheckedModeBanner: false, - theme: ThemeData( - brightness: Brightness.dark, - bottomNavigationBarTheme: - const BottomNavigationBarThemeData(selectedItemColor: Colors.white), - ), - home: NavScreen(), - ); + title: 'Flutter YouTube UI', + debugShowCheckedModeBanner: false, + theme: context.watch().currentTheme, + home: NavScreen()); } } diff --git a/lib/screens/library_screen.dart b/lib/screens/library_screen.dart index 953f987..fcab645 100644 --- a/lib/screens/library_screen.dart +++ b/lib/screens/library_screen.dart @@ -4,7 +4,28 @@ import 'package:hw_3/data/colors.dart'; import 'package:hw_3/widgets/app_bar.dart'; -class LibraryTab extends StatelessWidget { +class LibraryTab extends StatefulWidget { + LibraryState createState() => LibraryState(); +} + +class LibraryState extends State { + late String temp; + List playList = []; + int likeCounter = 0; + + @override + void initState() { + super.initState(); + + playList.addAll(['maks_playlist', 'vika_playlist', 'mykhailo_playlist']); + } + + void _incrementCounter() { + setState(() { + likeCounter++; + }); + } + @override Widget build(BuildContext context) { return Scaffold( @@ -19,61 +40,124 @@ class LibraryTab extends StatelessWidget { leading: Icon(Icons.history, color: suvaGrey), title: Text( 'History', - style: TextStyle(color: Colors.white), ), ), ListTile( leading: Icon(Icons.file_download, color: suvaGrey), - title: Text('Downloads', style: TextStyle(color: Colors.white)), + title: Text( + 'Downloads', + ), subtitle: Text('2 recommendations', style: TextStyle(color: suvaGrey, fontSize: 12.0)), ), ListTile( leading: Icon(Icons.video_library, color: suvaGrey), - title: - Text('Your videos', style: TextStyle(color: Colors.white)), + title: Text( + 'Your videos', + ), ), ListTile( leading: Icon(Icons.attach_money, color: suvaGrey), - title: Text('Purchases', style: TextStyle(color: Colors.white)), + title: Text('Purchases'), ), ListTile( leading: Icon(Icons.watch_later, color: suvaGrey), - title: - Text('Watch later', style: TextStyle(color: Colors.white)), + title: Text( + 'Watch later', + ), subtitle: Text('Videos you save for later', style: TextStyle(color: suvaGrey, fontSize: 12.0)), ), - Divider(color: Colors.white), + Divider(color: suvaGrey), Padding( - padding: EdgeInsets.symmetric(horizontal: 20.0, vertical: 10.0), + padding: + EdgeInsets.symmetric(horizontal: 20.0, vertical: 10.0), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Text('Playlists', - style: TextStyle(color: Colors.white, fontSize: 16.0)), + Text('Playlists', style: TextStyle(fontSize: 16.0)), Row( children: [ Text('Recently added', - style: - TextStyle(color: Colors.white, fontSize: 16.0)), - Icon(Icons.arrow_drop_down, color: Colors.white) + style: TextStyle(fontSize: 16.0)), + Icon(Icons.arrow_drop_down, color: suvaGrey) ], ) ], ), ), ListTile( + onTap: () { + showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + title: Text('Add playlist'), + content: TextField( + onChanged: (String value) { + temp = value; + }, + ), + actions: [ + ElevatedButton( + onPressed: () { + setState(() { + playList.add(temp); + }); + Navigator.of(context).pop(); + }, + child: Text('Add')) + ], + ); + }); + }, leading: Icon(Icons.add, color: linkBlue), - title: Text('New Playlist', style: TextStyle(color: linkBlue)), + title: + Text('New Playlist', style: TextStyle(color: linkBlue)), ), ListTile( + onTap: _incrementCounter, leading: Icon(Icons.thumb_up, color: suvaGrey), - title: - Text('Liked videos', style: TextStyle(color: Colors.white)), - subtitle: Text('4 Videos', + title: Text( + 'Liked videos', + ), + subtitle: Text('$likeCounter Videos', style: TextStyle(color: suvaGrey, fontSize: 12.0)), ), + ListView.builder( + shrinkWrap: true, + itemCount: playList.length, + itemBuilder: (BuildContext context, int index) { + return Dismissible( + key: Key(playList[index]), + child: Card( + child: ListTile( + subtitle: Text('$likeCounter Videos', + style: + TextStyle(color: suvaGrey, fontSize: 12.0)), + leading: + Icon(Icons.featured_play_list, color: suvaGrey), + title: Text(playList[index]), + trailing: IconButton( + icon: Icon( + Icons.delete_sweep, + color: Colors.red, + ), + onPressed: () { + setState(() { + playList.removeAt(index); + }); + }, + ), + ), + ), + onDismissed: (direction) { + setState(() { + playList.removeAt(index); + }); + }, + ); + }), ], ), ), diff --git a/lib/screens/user_profile_screen.dart b/lib/screens/user_profile_screen.dart index 1a49e4d..ee0bd1e 100644 --- a/lib/screens/user_profile_screen.dart +++ b/lib/screens/user_profile_screen.dart @@ -1,7 +1,11 @@ +import 'dart:math'; + import 'package:flutter/material.dart'; import 'package:hw_3/data/colors.dart'; import 'package:hw_3/data/data.dart'; +import 'package:hw_3/globalStateManagement/userImageManagement.dart'; +import 'package:provider/src/provider.dart'; class UserProfilePage extends StatelessWidget { final Channel currentUser; @@ -30,10 +34,18 @@ class UserProfilePage extends StatelessWidget { padding: EdgeInsets.all(20.0), child: Row( children: [ - CircleAvatar( - backgroundImage: - AssetImage(currentUser.logoImagePath), - radius: 30), + GestureDetector( + onTap: () { + context.read().generateRandomImage(); + }, + child: Builder( + builder: (context) { + return CircleAvatar( + backgroundImage: NetworkImage(context.watch().randomImage), + radius: 30); + } + ), + ), SizedBox(width: 10.0), Column( crossAxisAlignment: CrossAxisAlignment.start, diff --git a/lib/widgets/app_bar.dart b/lib/widgets/app_bar.dart index 5d16605..d77fac6 100644 --- a/lib/widgets/app_bar.dart +++ b/lib/widgets/app_bar.dart @@ -1,8 +1,11 @@ import 'package:flutter/material.dart'; import 'package:hw_3/data/data.dart'; +import 'package:hw_3/globalStateManagement/themeManagement.dart'; +import 'package:hw_3/globalStateManagement/userImageManagement.dart'; import 'package:hw_3/screens/user_profile_screen.dart'; +import 'package:provider/src/provider.dart'; class CustomAppBar extends StatelessWidget implements PreferredSizeWidget { @@ -20,6 +23,12 @@ class CustomAppBar extends StatelessWidget child: Image.asset('assets/images/home_screen/logos/logo_dark.png'), ), actions: [ + IconButton( + icon: const Icon(Icons.phonelink_setup), + onPressed: () { + context.read().toggleTheme(); + }, + ), IconButton( icon: const Icon(Icons.cast), onPressed: () {}, @@ -32,18 +41,22 @@ class CustomAppBar extends StatelessWidget icon: const Icon(Icons.search), onPressed: () {}, ), - IconButton( - icon: CircleAvatar( - foregroundImage: NetworkImage(currentUser.imageUrl), - ), - onPressed: () { - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => UserProfilePage(currentUser: currentUser), - ), - ); - }, + Builder( + builder: (context) { + return IconButton( + icon: CircleAvatar( + foregroundImage: NetworkImage(context.watch().randomImage), + ), + onPressed: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => UserProfilePage(currentUser: currentUser), + ), + ); + }, + ); + } ), ], ); diff --git a/lib/widgets/video_card.dart b/lib/widgets/video_card.dart index 378a0e7..b626788 100644 --- a/lib/widgets/video_card.dart +++ b/lib/widgets/video_card.dart @@ -1,4 +1,6 @@ import 'package:flutter/material.dart'; +import 'package:hw_3/globalStateManagement/userImageManagement.dart'; +import 'package:provider/src/provider.dart'; import 'package:timeago/timeago.dart' as timeago; import 'package:hw_3/data/data.dart'; @@ -7,11 +9,27 @@ import 'package:hw_3/screens/video_detail_screen.dart'; import 'package:hw_3/widgets/util.dart'; -class VideoCard extends StatelessWidget { +class VideoCard extends StatefulWidget { final Video video; const VideoCard({Key? key, required this.video}) : super(key: key); + @override + _VideoCardState createState() => + _VideoCardState(views: this.video.viewsCounter); +} + +class _VideoCardState extends State { + int views; + + _VideoCardState({required this.views}); + + void _incrementViews() { + setState(() { + views += 1000; + }); + } + @override Widget build(BuildContext context) { return Column( @@ -28,7 +46,7 @@ class VideoCard extends StatelessWidget { ); }, child: Image.network( - video.miniatureImagePath, + widget.video.miniatureImagePath, height: 220.0, width: double.infinity, fit: BoxFit.cover, @@ -41,7 +59,7 @@ class VideoCard extends StatelessWidget { padding: const EdgeInsets.all(4.0), color: Colors.black, child: Text( - video.duration, + widget.video.duration, style: Theme.of(context) .textTheme .caption! @@ -59,9 +77,12 @@ class VideoCard extends StatelessWidget { children: [ GestureDetector( onTap: () => print('Navigate to profile'), - child: CircleAvatar( - foregroundImage: NetworkImage(video.channel.imageUrl), - ), + child: Builder(builder: (context) { + return CircleAvatar( + foregroundImage: NetworkImage( + context.watch().randomImage), + ); + }), ), const SizedBox(width: 8.0), Expanded( @@ -71,7 +92,7 @@ class VideoCard extends StatelessWidget { children: [ Flexible( child: Text( - video.title, + widget.video.title, maxLines: 2, overflow: TextOverflow.ellipsis, style: Theme.of(context) @@ -81,16 +102,21 @@ class VideoCard extends StatelessWidget { ), ), Flexible( - child: Text( - '${video.channel.name} • ' - '${formatNumber(video.viewsCounter)} • ' - '${timeago.format(video.timestamp)}', - maxLines: 2, - overflow: TextOverflow.ellipsis, - style: Theme.of(context) - .textTheme - .caption! - .copyWith(fontSize: 14.0), + child: GestureDetector( + onTap: () { + _incrementViews(); + }, + child: Text( + '${widget.video.channel.name} • ' + '${formatNumber(views)} • ' + '${timeago.format(widget.video.timestamp)}', + maxLines: 2, + overflow: TextOverflow.ellipsis, + style: Theme.of(context) + .textTheme + .caption! + .copyWith(fontSize: 14.0), + ), ), ), ], diff --git a/pubspec.lock b/pubspec.lock index a8e5f78..4ba5760 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -95,6 +95,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.7.0" + nested: + dependency: transitive + description: + name: nested + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.0" path: dependency: transitive description: @@ -102,6 +109,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.8.0" + provider: + dependency: "direct main" + description: + name: provider + url: "https://pub.dartlang.org" + source: hosted + version: "6.0.1" sky_engine: dependency: transitive description: flutter @@ -172,3 +186,4 @@ packages: version: "2.1.0" sdks: dart: ">=2.12.0 <3.0.0" + flutter: ">=1.16.0" diff --git a/pubspec.yaml b/pubspec.yaml index fe03bef..ddeac34 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -35,6 +35,7 @@ dependencies: # Use with the CupertinoIcons class for iOS style icons. cupertino_icons: ^1.0.2 timeago: ^3.1.0 + provider: ^6.0.1 dev_dependencies: flutter_test: