From e290274a957061ab484571b37687fd20a5ab0cdb Mon Sep 17 00:00:00 2001 From: Eric Ampire Date: Thu, 25 Mar 2021 10:54:09 +0200 Subject: [PATCH] Adding TopNews --- ios/Podfile.lock | 6 + lib/model/news.dart | 35 +++++ lib/ui/custom/item_mag_large.dart | 1 + lib/ui/custom/item_mag_row.dart | 1 + lib/ui/custom/item_mag_row_newest.dart | 3 +- lib/ui/custom/item_mag_small.dart | 1 + lib/ui/view/main.dart | 5 +- lib/ui/view/news.dart | 8 -- lib/ui/view/news/item_regular_news.dart | 130 ++++++++++++++++++ lib/ui/view/news/item_top_news.dart | 85 ++++++++++++ lib/ui/view/news/news.dart | 74 ++++++++++ lib/ui/view/profile.dart | 19 +-- lib/ui/view/search/item_mag_large_search.dart | 67 +++++++++ lib/ui/view/search/item_mag_search.dart | 1 + lib/ui/view/search/search.dart | 119 +++++++++++++--- lib/ui/view/setting/setting.dart | 1 + pubspec.lock | 4 +- 17 files changed, 510 insertions(+), 50 deletions(-) create mode 100644 lib/model/news.dart delete mode 100644 lib/ui/view/news.dart create mode 100644 lib/ui/view/news/item_regular_news.dart create mode 100644 lib/ui/view/news/item_top_news.dart create mode 100644 lib/ui/view/news/news.dart create mode 100644 lib/ui/view/search/item_mag_large_search.dart diff --git a/ios/Podfile.lock b/ios/Podfile.lock index a33018c..f8d2e09 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -95,6 +95,8 @@ PODS: - sqflite (0.0.2): - Flutter - FMDB (>= 2.7.5) + - url_launcher (0.0.1): + - Flutter DEPENDENCIES: - advance_pdf_viewer (from `.symlinks/plugins/advance_pdf_viewer/ios`) @@ -106,6 +108,7 @@ DEPENDENCIES: - path_provider (from `.symlinks/plugins/path_provider/ios`) - shared_preferences (from `.symlinks/plugins/shared_preferences/ios`) - sqflite (from `.symlinks/plugins/sqflite/ios`) + - url_launcher (from `.symlinks/plugins/url_launcher/ios`) SPEC REPOS: trunk: @@ -144,6 +147,8 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/shared_preferences/ios" sqflite: :path: ".symlinks/plugins/sqflite/ios" + url_launcher: + :path: ".symlinks/plugins/url_launcher/ios" SPEC CHECKSUMS: advance_pdf_viewer: 5c832f382b4ca84fe735f893b7e91aa830c27d7b @@ -170,6 +175,7 @@ SPEC CHECKSUMS: PromisesObjC: 3113f7f76903778cf4a0586bd1ab89329a0b7b97 shared_preferences: af6bfa751691cdc24be3045c43ec037377ada40d sqflite: 6d358c025f5b867b29ed92fc697fd34924e11904 + url_launcher: 6fef411d543ceb26efce54b05a0a40bfd74cbbef PODFILE CHECKSUM: aafe91acc616949ddb318b77800a7f51bffa2a4c diff --git a/lib/model/news.dart b/lib/model/news.dart new file mode 100644 index 0000000..9abd19b --- /dev/null +++ b/lib/model/news.dart @@ -0,0 +1,35 @@ +class News { + final String uid; + final String title; + final DateTime createdAt; + final DateTime updatedAt; + final String previewUrl; + final String sourceUrl; + final DateTime publishedAt; + final String fullPostUrl; + + News({ + this.uid, + this.title, + this.createdAt, + this.updatedAt, + this.previewUrl, + this.sourceUrl, + this.publishedAt, + this.fullPostUrl, + }); +} + + +final title = "Epoxy is an Android library for building complex screens in a RecyclerView. It abstracts the boilerplate of view holders, item types, item ids, span counts, and more, in order to simplify building screens with multiple view types. Additionally, Epoxy adds support for saving view state and automatic diffing of item changes."; +final sourceUrl = "https://firebasestorage.googleapis.com/v0/b/discover-backend.appspot.com/o/providers%2Fcm%2Fsource.png?alt=media&token=c05913ac-07e0-4402-9fc8-2fc6cc484440"; +final url = "https://s.france24.com/media/display/be59c500-68ea-11eb-aefa-005056a98db9/w:1280/p:16x9/e8bbac6ad2d9bac2d5d6410a5394a177fc978f78.webp"; +final newsTest = [ + News(uid: "1", title: title, previewUrl: url, sourceUrl: sourceUrl), + News(uid: "2", title: title, previewUrl: url, sourceUrl: sourceUrl), + News(uid: "3", title: title, previewUrl: url, sourceUrl: sourceUrl), + News(uid: "4", title: title, previewUrl: url, sourceUrl: sourceUrl), + News(uid: "5", title: title, previewUrl: url, sourceUrl: sourceUrl), + News(uid: "6", title: title, previewUrl: url, sourceUrl: sourceUrl), + News(uid: "7", title: title, previewUrl: url, sourceUrl: sourceUrl), +]; \ No newline at end of file diff --git a/lib/ui/custom/item_mag_large.dart b/lib/ui/custom/item_mag_large.dart index 1d94287..14fd768 100644 --- a/lib/ui/custom/item_mag_large.dart +++ b/lib/ui/custom/item_mag_large.dart @@ -32,6 +32,7 @@ class ItemMagLarge extends StatelessWidget { height: 235, width: 168, decoration: BoxDecoration( + color: Colors.grey, shape: BoxShape.rectangle, borderRadius: BorderRadius.all( Radius.circular(5), diff --git a/lib/ui/custom/item_mag_row.dart b/lib/ui/custom/item_mag_row.dart index 3a7d699..e7492d2 100644 --- a/lib/ui/custom/item_mag_row.dart +++ b/lib/ui/custom/item_mag_row.dart @@ -49,6 +49,7 @@ class ItemMagRow extends StatelessWidget { rowTitle, style: TextStyle( fontSize: 22, + fontWeight: FontWeight.bold, ), ), InkWell( diff --git a/lib/ui/custom/item_mag_row_newest.dart b/lib/ui/custom/item_mag_row_newest.dart index de30431..c9419cf 100644 --- a/lib/ui/custom/item_mag_row_newest.dart +++ b/lib/ui/custom/item_mag_row_newest.dart @@ -31,6 +31,7 @@ class ItemMagRowNewest extends StatelessWidget { Text( title, style: TextStyle( + fontWeight: FontWeight.bold, fontSize: 22, ), ), @@ -62,7 +63,7 @@ class ItemMagRowNewest extends StatelessWidget { ); }, ), - ) + ), ], ), ); diff --git a/lib/ui/custom/item_mag_small.dart b/lib/ui/custom/item_mag_small.dart index a007460..86cda99 100644 --- a/lib/ui/custom/item_mag_small.dart +++ b/lib/ui/custom/item_mag_small.dart @@ -18,6 +18,7 @@ class ItemMagSmall extends StatelessWidget { height: 167, width: 119, decoration: BoxDecoration( + color: Colors.grey, shape: BoxShape.rectangle, borderRadius: BorderRadius.all(Radius.circular(5)), image: DecorationImage( diff --git a/lib/ui/view/main.dart b/lib/ui/view/main.dart index 031a6f6..75374e2 100644 --- a/lib/ui/view/main.dart +++ b/lib/ui/view/main.dart @@ -1,8 +1,9 @@ import 'package:firebase_auth/firebase_auth.dart'; import 'package:flutter/material.dart'; import 'package:flutter/widgets.dart'; +import 'package:le_kiosque_by_gcs/model/news.dart'; import 'package:le_kiosque_by_gcs/ui/view/home.dart'; -import 'package:le_kiosque_by_gcs/ui/view/news.dart'; +import 'package:le_kiosque_by_gcs/ui/view/news/news.dart'; import 'package:le_kiosque_by_gcs/ui/view/profile.dart'; import 'package:le_kiosque_by_gcs/ui/view/search/search.dart'; import 'package:le_kiosque_by_gcs/ui/view/setting/setting.dart'; @@ -18,7 +19,7 @@ class _MainViewState extends State { List _widgetOptions = [ HomeView(), - NewsView(), + NewsView(newsList: newsTest), SubscriptionView(), ProfileView() ]; diff --git a/lib/ui/view/news.dart b/lib/ui/view/news.dart deleted file mode 100644 index b7aedc1..0000000 --- a/lib/ui/view/news.dart +++ /dev/null @@ -1,8 +0,0 @@ -import 'package:flutter/cupertino.dart'; - -class NewsView extends StatelessWidget { - @override - Widget build(BuildContext context) { - return Container(); - } -} diff --git a/lib/ui/view/news/item_regular_news.dart b/lib/ui/view/news/item_regular_news.dart new file mode 100644 index 0000000..6027057 --- /dev/null +++ b/lib/ui/view/news/item_regular_news.dart @@ -0,0 +1,130 @@ +import 'package:flutter/material.dart'; +import 'package:le_kiosque_by_gcs/model/news.dart'; + +class ItemRegularNews extends StatelessWidget { + const ItemRegularNews({Key key, this.news}) : super(key: key); + + final News news; + + @override + Widget build(BuildContext context) { + return Padding( + padding: const EdgeInsets.only(left: 12, right: 12), + child: InkWell( + onTap: () { + _showDetailNews(context); + }, + child: Card( + elevation: 4, + child: Container( + child: Row( + children: [ + Padding( + padding: const EdgeInsets.all(10.0), + child: Container( + height: 90, + width: 90, + decoration: BoxDecoration( + color: Colors.grey, + shape: BoxShape.rectangle, + borderRadius: BorderRadius.all( + Radius.circular(10), + ), + image: DecorationImage( + image: NetworkImage(news.previewUrl), + fit: BoxFit.cover, + ), + ), + ), + ), + Expanded( + child: Column( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: const EdgeInsets.only(right: 10), + child: Text( + news.title, + textAlign: TextAlign.justify, + overflow: TextOverflow.ellipsis, + maxLines: 3, + style: TextStyle( + fontSize: 15, + color: Colors.black, + ), + ), + ), + SizedBox(height: 14), + SizedBox( + height: 16, + child: Image.network(news.sourceUrl), + ) + ], + ), + ) + ], + ), + ), + ), + ), + ); + return InkWell( + onTap: () { + _showDetailNews(context); + }, + child: Padding( + padding: const EdgeInsets.only(left: 16, right: 16), + child: Card( + elevation: 4, + child: Container( + width: double.infinity, + height: 120, + child: Row( + children: [ + Container( + height: 100, + width: 100, + decoration: BoxDecoration( + color: Colors.grey, + shape: BoxShape.rectangle, + borderRadius: BorderRadius.all( + Radius.circular(10), + ), + image: DecorationImage( + image: NetworkImage(news.previewUrl), + fit: BoxFit.cover, + ), + ), + ), + Column( + children: [ + Padding( + padding: EdgeInsets.all(16.0), + child: Text( + news.title, + overflow: TextOverflow.ellipsis, + maxLines: 4, + style: TextStyle( + fontSize: 17, + color: Colors.black, + ), + ), + ), + Padding( + padding: const EdgeInsets.all(16), + child: Image.network(news.sourceUrl), + ) + ], + ) + ], + ), + ), + ), + ), + ); + } + + void _showDetailNews(BuildContext context) {} +} diff --git a/lib/ui/view/news/item_top_news.dart b/lib/ui/view/news/item_top_news.dart new file mode 100644 index 0000000..c96ce13 --- /dev/null +++ b/lib/ui/view/news/item_top_news.dart @@ -0,0 +1,85 @@ +import 'package:flutter/material.dart'; +import 'package:le_kiosque_by_gcs/model/news.dart'; +import 'package:url_launcher/url_launcher.dart'; + +class ItemTopNews extends StatelessWidget { + const ItemTopNews({Key key, this.news}) : super(key: key); + + final News news; + + @override + Widget build(BuildContext context) { + return Container( + height: 259, + width: 270, + child: InkWell( + onTap: () { + _showFullArticle(context, news.fullPostUrl); + }, + child: Stack( + alignment: Alignment.bottomLeft, + children: [ + Container( + height: 259, + width: 270, + decoration: BoxDecoration( + color: Colors.grey, + shape: BoxShape.rectangle, + borderRadius: BorderRadius.all( + Radius.circular(10), + ), + image: DecorationImage( + image: NetworkImage(news.previewUrl), + fit: BoxFit.cover, + ), + ), + ), + Container( + height: 259, + width: 270, + decoration: BoxDecoration( + color: Colors.black38, + shape: BoxShape.rectangle, + borderRadius: BorderRadius.all( + Radius.circular(10), + ), + ), + ), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + Padding( + padding: const EdgeInsets.all(16.0), + child: Text( + news.title, + maxLines: 4, + style: TextStyle( + fontSize: 17, + color: Colors.white, + ), + ), + ), + Padding( + padding: const EdgeInsets.only(left: 16, bottom: 16), + child: SizedBox( + height: 16, + child: Image.network(news.sourceUrl), + ), + ) + ], + ), + ], + ), + ), + ); + } + + void _showFullArticle(BuildContext context, String fullPostUrl) async { + if (await canLaunch(fullPostUrl)) { + await launch(fullPostUrl); + } else { + throw 'Could not launch $fullPostUrl'; + } + } +} diff --git a/lib/ui/view/news/news.dart b/lib/ui/view/news/news.dart new file mode 100644 index 0000000..f803bd9 --- /dev/null +++ b/lib/ui/view/news/news.dart @@ -0,0 +1,74 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:le_kiosque_by_gcs/model/magazine.dart'; +import 'package:le_kiosque_by_gcs/model/news.dart'; +import 'package:le_kiosque_by_gcs/ui/view/news/item_regular_news.dart'; +import 'package:le_kiosque_by_gcs/ui/view/news/item_top_news.dart'; + +class NewsView extends StatelessWidget { + const NewsView({ + Key key, + @required this.newsList, + }) : super(key: key); + + final List newsList; + + @override + Widget build(BuildContext context) { + return CustomScrollView( + slivers: [ + SliverPadding( + padding: EdgeInsets.all(16), + sliver: SliverToBoxAdapter( + child: Text( + "Top actualité", + style: TextStyle( + fontSize: 22, + fontWeight: FontWeight.bold, + color: Color(0xFF3B3B3B), + ), + ), + ), + ), + SliverToBoxAdapter( + child: Container( + height: 280, + child: ListView.builder( + scrollDirection: Axis.horizontal, + itemCount: magazinesTest.length, + itemBuilder: (context, index) { + return Padding( + padding: const EdgeInsets.only( + left: 16, + bottom: 16, + ), + child: ItemTopNews( + news: newsList[index], + ), + ); + }, + ), + ), + ), + SliverPadding( + padding: EdgeInsets.all(16), + sliver: SliverToBoxAdapter( + child: Text( + "Actualité de la semaine", + style: TextStyle( + fontSize: 22, + fontWeight: FontWeight.bold, + color: Color(0xFF3B3B3B), + ), + ), + ), + ), + SliverList( + delegate: SliverChildBuilderDelegate((context, index) { + return ItemRegularNews(news: newsList[index]); + }, childCount: newsList.length), + ), + ], + ); + } +} diff --git a/lib/ui/view/profile.dart b/lib/ui/view/profile.dart index 306106f..632e8e0 100644 --- a/lib/ui/view/profile.dart +++ b/lib/ui/view/profile.dart @@ -13,20 +13,7 @@ class ProfileView extends StatefulWidget { class _ProfileViewState extends State { @override Widget build(BuildContext context) { - // TODO Removig - final summary = - "Voyez ce jeu exquis wallon, de graphie en kit mais bref. Portez ce vieux whisky au juge blond qui fume sur son île intérieure, à côté de l\"alcôve ovoïde, où les bûches se consument dans l\"âtre"; - final url = - "https://miningandbusiness.com/wp-content/uploads/2021/02/03f19c3a-1d92-4be7-ac54-f52b37e626ad-561x771.jpg"; - final urlPrd = "http://www.africau.edu/images/default/sample.pdf"; - - final magazines = [ - Magazine(urlCover: url, magazineUrl: urlPrd, summaryText: summary), - Magazine(urlCover: url, magazineUrl: urlPrd, summaryText: summary), - Magazine(urlCover: url, magazineUrl: urlPrd, summaryText: summary), - Magazine(urlCover: url, magazineUrl: urlPrd, summaryText: summary), - Magazine(urlCover: url, magazineUrl: urlPrd, summaryText: summary) - ]; + // TODO Removing return Scaffold( body: CustomScrollView( slivers: [ @@ -64,8 +51,8 @@ class _ProfileViewState extends State { mainAxisExtent: 250 ), delegate: SliverChildBuilderDelegate( - (context, int index) => ItemMagLarge(magazine: magazines[index]), - childCount: magazines.length, + (context, int index) => ItemMagLarge(magazine: magazinesTest[index]), + childCount: magazinesTest.length, ), ), ), diff --git a/lib/ui/view/search/item_mag_large_search.dart b/lib/ui/view/search/item_mag_large_search.dart new file mode 100644 index 0000000..f13f8cf --- /dev/null +++ b/lib/ui/view/search/item_mag_large_search.dart @@ -0,0 +1,67 @@ +import 'package:flutter/material.dart'; +import 'package:le_kiosque_by_gcs/model/magazine.dart'; +import 'package:le_kiosque_by_gcs/ui/view/pdf_reader.dart'; + +class ItemMagLargeSearch extends StatelessWidget { + const ItemMagLargeSearch({ + Key key, + @required this.magazine, + }) : super(key: key); + + final Magazine magazine; + + void showPdfReader(BuildContext context, String magazineUrl) { + Navigator.of(context).push( + MaterialPageRoute( + builder: (context) { + return PdfReader( + pdfUrl: magazine.magazineUrl, + ); + }, + ), + ); + } + + @override + Widget build(BuildContext context) { + return Stack( + children: [ + InkWell( + onTap: () { + showPdfReader(context, magazine.magazineUrl); + }, + child: Container( + height: 235, + width: 168, + decoration: BoxDecoration( + shape: BoxShape.rectangle, + borderRadius: BorderRadius.all( + Radius.circular(5), + ), + image: DecorationImage( + image: NetworkImage(magazine.urlCover), + fit: BoxFit.cover, + ), + ), + ), + ), + Padding( + padding: const EdgeInsets.all(8.0), + child: Container( + decoration: BoxDecoration( + shape: BoxShape.circle, + color: Colors.black54, + ), + child: Padding( + padding: const EdgeInsets.all(4.0), + child: Icon( + Icons.close_rounded, + color: Colors.white, + ), + ), + ), + ), + ], + ); + } +} diff --git a/lib/ui/view/search/item_mag_search.dart b/lib/ui/view/search/item_mag_search.dart index 35d8484..40ae4e0 100644 --- a/lib/ui/view/search/item_mag_search.dart +++ b/lib/ui/view/search/item_mag_search.dart @@ -22,6 +22,7 @@ class ItemMagazineSearch extends StatelessWidget { height: 50, width: 50, decoration: BoxDecoration( + color: Colors.grey, shape: BoxShape.circle, image: DecorationImage( image: NetworkImage(magazine.urlCover), diff --git a/lib/ui/view/search/search.dart b/lib/ui/view/search/search.dart index e360d75..8dddb30 100644 --- a/lib/ui/view/search/search.dart +++ b/lib/ui/view/search/search.dart @@ -1,9 +1,19 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:le_kiosque_by_gcs/model/magazine.dart'; +import 'package:le_kiosque_by_gcs/ui/custom/item_mag_large.dart'; import 'package:le_kiosque_by_gcs/ui/view/search/item_mag_search.dart'; -class SearchView extends StatelessWidget { +import 'item_mag_large_search.dart'; + +class SearchView extends StatefulWidget { + @override + _SearchViewState createState() => _SearchViewState(); +} + +class _SearchViewState extends State { + bool _isGrid = false; + @override Widget build(BuildContext context) { // TODO: Removing test data @@ -40,27 +50,94 @@ class SearchView extends StatelessWidget { ), ), ), - body: ListView.builder( - itemCount: magazinesTest.length + 1, - itemBuilder: (context, index) { - if (index == 0) { - return Padding( - padding: const EdgeInsets.all(16.0), - child: Text( - "Recherches récentes", - style: TextStyle( - fontSize: 22, - color: Color(0xFF3B3B3B), + body: _isGrid ? _buildGrid() : _buildList(), + ); + } + + Widget _buildList() { + return ListView.builder( + itemCount: magazinesTest.length + 1, + itemBuilder: (context, index) { + if (index == 0) { + return Padding( + padding: const EdgeInsets.all(16.0), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + "Recherches récentes", + style: TextStyle( + fontSize: 22, + color: Color(0xFF3B3B3B), + ), ), - ), - ); - } else { - return ItemMagazineSearch( - magazine: magazinesTest[index - 1], - ); - } - }, - ), + InkWell( + onTap: () => _updateListAppearance(), + child: Icon( + _isGrid ? Icons.list : Icons.grid_view, + ), + ) + ], + ), + ); + } else { + return ItemMagazineSearch( + magazine: magazinesTest[index - 1], + ); + } + }, + ); + } + + _updateListAppearance() { + setState(() { + _isGrid = !_isGrid; + }); + } + + Widget _buildGrid() { + return CustomScrollView( + slivers: [ + SliverToBoxAdapter( + child: Padding( + padding: const EdgeInsets.all(16.0), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + "Recherches récentes", + style: TextStyle( + fontSize: 22, + color: Color(0xFF3B3B3B), + ), + ), + InkWell( + onTap: () => _updateListAppearance(), + child: Icon( + _isGrid ? Icons.list : Icons.grid_view, + ), + ) + ], + ), + ), + ), + SliverPadding( + padding: EdgeInsets.all(16), + sliver: SliverGrid( + gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: 2, + mainAxisSpacing: 16.0, + crossAxisSpacing: 16.0, + mainAxisExtent: 250, + ), + delegate: SliverChildBuilderDelegate( + (context, int index) => + ItemMagLargeSearch(magazine: magazinesTest[index]), + childCount: magazinesTest.length, + ), + ), + ), + ], ); } } diff --git a/lib/ui/view/setting/setting.dart b/lib/ui/view/setting/setting.dart index 9c81c25..d1fafaf 100644 --- a/lib/ui/view/setting/setting.dart +++ b/lib/ui/view/setting/setting.dart @@ -22,6 +22,7 @@ class SettingView extends StatelessWidget { ); } + // Todo: remplace final String licenceUrl = "http://example.com"; _showTermAndCond(BuildContext context) async { diff --git a/pubspec.lock b/pubspec.lock index a720f79..78e0e0c 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -365,7 +365,7 @@ packages: name: process url: "https://pub.dartlang.org" source: hosted - version: "4.1.0" + version: "4.2.1" provider: dependency: "direct main" description: @@ -552,7 +552,7 @@ packages: name: uuid url: "https://pub.dartlang.org" source: hosted - version: "3.0.1" + version: "3.0.2" vector_math: dependency: transitive description: