diff --git a/android/build.gradle b/android/build.gradle index e50c3a0..b758fd1 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -3,6 +3,8 @@ buildscript { repositories { google() mavenCentral() + maven { url 'https://maven.aliyun.com/repository/central' } + maven { url 'https://maven.aliyun.com/repository/public/' } } dependencies { @@ -15,6 +17,9 @@ allprojects { repositories { google() mavenCentral() + + maven { url 'https://maven.aliyun.com/repository/central' } + maven { url 'https://maven.aliyun.com/repository/public/' } } } diff --git a/android/gradle.properties b/android/gradle.properties index 6a7fcc5..3e64fae 100644 --- a/android/gradle.properties +++ b/android/gradle.properties @@ -1,4 +1,10 @@ org.gradle.jvmargs=-Xmx1536M android.useAndroidX=true android.enableJetifier=true -#org.gradle.java.home=D\:\\Android\\Android Studio\\jbr \ No newline at end of file +#org.gradle.java.home=D\:\\Android\\Android Studio\\jbr + +# 配置 VPN 加快下载速度 +systemProp.http.proxyHost=127.0.0.1 +systemProp.http.proxyPort=7890 +systemProp.https.proxyHost=127.0.0.1 +systemProp.https.proxyPort=7890 \ No newline at end of file diff --git a/lib/config/server.dart b/lib/config/server.dart index 700de6c..d626f1a 100644 --- a/lib/config/server.dart +++ b/lib/config/server.dart @@ -1,4 +1,5 @@ /// Api地址 -const String ServerApiUrl = "http://172.31.163.87:4523/m1/2998542-0-default"; +// const String ServerApiUrl = "http://172.31.163.87:4523/m1/2998542-0-default"; +const String ServerApiUrl = "http://192.168.1.5:4523/m1/2998542-0-default"; const CryptoSalt = "E1pWsyfiy@R@X#qn17!StJNdZK1fFF8iV6ffN!goZkqt#JxO"; \ No newline at end of file diff --git a/lib/modules/application/application_page.dart b/lib/modules/application/application_page.dart index 8806ba3..9716a0e 100644 --- a/lib/modules/application/application_page.dart +++ b/lib/modules/application/application_page.dart @@ -44,7 +44,7 @@ class ApplicationPage extends GetView { onPageChanged: controller.handlePageChange, children: [ MainPage(), - Text('Categories'), + CategoryPage(), Text('BookmarksPage'), Text('AccountPage'), ], @@ -68,6 +68,7 @@ class ApplicationPage extends GetView { @override Widget build(BuildContext context) { return Scaffold( + backgroundColor: Color(0xfff6f6f6), appBar: _buildAppBar(), body: _buildPageView(), bottomNavigationBar: _buildBottomNavigationBar(), diff --git a/lib/modules/category/category_page.dart b/lib/modules/category/category_page.dart index a795815..06b9867 100644 --- a/lib/modules/category/category_page.dart +++ b/lib/modules/category/category_page.dart @@ -3,13 +3,11 @@ import 'package:get/get.dart'; import 'category_controller.dart'; -class CategoryPage extends StatelessWidget { +class CategoryPage extends GetView { const CategoryPage({Key? key}) : super(key: key); @override Widget build(BuildContext context) { - final controller = Get.find(); - - return Container(); + return Text('Categories'); } } diff --git a/lib/modules/main/main_controller.dart b/lib/modules/main/main_controller.dart index c319493..f17ae96 100644 --- a/lib/modules/main/main_controller.dart +++ b/lib/modules/main/main_controller.dart @@ -19,6 +19,7 @@ class MainController extends GetxController { /// 方法 // 拉取数据 asyncLoadAllData() async { + print('开始拉取数据'); state.categories = await newsRepository.categories(cacheDisk: true); state.channels = await newsRepository.channels(cacheDisk: true); // 分类对应的数据(推荐、新闻) diff --git a/lib/modules/main/main_page.dart b/lib/modules/main/main_page.dart index 5129bc9..98961d1 100644 --- a/lib/modules/main/main_page.dart +++ b/lib/modules/main/main_page.dart @@ -1,6 +1,9 @@ import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:news_getx/modules/main/widgets/categories.dart'; +import 'package:news_getx/modules/main/widgets/channels.dart'; +import 'package:news_getx/modules/main/widgets/news_letter.dart'; +import 'package:news_getx/modules/main/widgets/news_list.dart'; import 'package:news_getx/modules/main/widgets/recommend.dart'; import 'main_controller.dart'; @@ -17,11 +20,11 @@ class MainPage extends GetView { Divider(height: 1), NewsRecommendWidget(), Divider(height: 1), - Text('NewsChannelsWidget'), + NewsChannelsWidget(), Divider(height: 1), - Text('NewsListWidget'), + NewsListWidget(), Divider(height: 1), - Text('NewsletterWidget'), + NewsLetterWidget(), ], ), ); diff --git a/lib/modules/main/widgets/ad.dart b/lib/modules/main/widgets/ad.dart new file mode 100644 index 0000000..903e7b6 --- /dev/null +++ b/lib/modules/main/widgets/ad.dart @@ -0,0 +1,41 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:get/get.dart'; +import 'package:news_getx/modules/main/main_controller.dart'; +import 'package:news_getx/theme/app_borders.dart'; +import 'package:news_getx/theme/app_colors.dart'; +import 'package:news_getx/theme/app_radii.dart'; + +class AdWidget extends GetView{ + @override + Widget build(BuildContext context) { + return Container( + height: 100.h, + padding: EdgeInsets.all(20.w), + child: Container( + padding: EdgeInsets.symmetric(horizontal: 20.w), + decoration: BoxDecoration( + // border + border: Border.fromBorderSide(AppBorders.primaryBorder), + borderRadius: Radii.k6pxRadius, + ), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + "Tired of Ads? Get Premium - \$9.99", + textAlign: TextAlign.center, + style: TextStyle( + color: AppColors.primaryText, + fontFamily: "Avenir", + fontWeight: FontWeight.w400, + fontSize: 18.sp, + height: 18.sp / 18, + ), + ), + ], + ), + ), + ); + } +} \ No newline at end of file diff --git a/lib/modules/main/widgets/channels.dart b/lib/modules/main/widgets/channels.dart new file mode 100644 index 0000000..11d833d --- /dev/null +++ b/lib/modules/main/widgets/channels.dart @@ -0,0 +1,86 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:get/get.dart'; +import 'package:news_getx/data/model/channels.dart'; +import 'package:news_getx/modules/main/main_controller.dart'; +import 'package:news_getx/theme/app_colors.dart'; +import 'package:news_getx/theme/app_shadows.dart'; + +class NewsChannelsWidget extends GetView { + Widget _buildChannelItem(ChannelResponse item) { + return Container( + margin: EdgeInsets.symmetric(horizontal: 10), + width: 70.w, + height: 97.h, + child: InkWell( + // 没有水波纹 是有原因的 看splashColor文档 主体splashColor是透明纯黑色 所以不显示 + // splashColor: Colors.cyanAccent, + onTap: () { + print(item.title); + }, + child: Column( + children: [ + // 当 `child` 是图片这种有具体宽高的组件时, + // 如果 `Container` 的宽高只设置期中一个, + // 则另一个的值会根据 `child` 的宽高比例计算得出, + // 如果是大于图片对应的宽高, 则图片原宽高居中显示, + // 此时 `Container` 留有空白. + // https://juejin.cn/post/7016649859008725022#heading-9 + Container( + width: 64.w, + height: 64.w, + margin: EdgeInsets.symmetric(horizontal: 3.w), + // 之所以设置padding而不是居中对齐 + // 是因为如果设置居中对齐的话展示的是图片本身的大小,而不是我们需要的内边距大小 + padding: EdgeInsets.all(10.w), + decoration: BoxDecoration( + color: AppColors.primaryBackground, + boxShadow: [AppShadows.primaryShadow], + borderRadius: BorderRadius.all(Radius.circular(32.w)), + ), + child: Image.asset( + "assets/images/channel-${item.code}.png", + fit: BoxFit.cover, + ), + ), + Padding( + padding: const EdgeInsets.only(top: 12.0), + child: Text( + item.title, + textAlign: TextAlign.center, + overflow: TextOverflow.clip, + maxLines: 1, + style: TextStyle( + color: AppColors.thirdElementText, + fontFamily: "Avenir", + fontWeight: FontWeight.w400, + fontSize: 14.sp, + height: 14.sp / 14, + ), + ), + ), + ], + ), + ), + ); + } + + @override + Widget build(BuildContext context) { + return Obx( + () => controller.state.channels == null + ? Container() + : Container( + height: 137.h, + child: SingleChildScrollView( + scrollDirection: Axis.horizontal, + child: Row( + children: controller.state.channels!.map((item) { + return _buildChannelItem(item); + }).toList(), + ), + ), + ), + ); + } +} diff --git a/lib/modules/main/widgets/news_letter.dart b/lib/modules/main/widgets/news_letter.dart new file mode 100644 index 0000000..cf1f1e8 --- /dev/null +++ b/lib/modules/main/widgets/news_letter.dart @@ -0,0 +1,102 @@ +import 'package:flutter/gestures.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:get/get.dart'; +import 'package:news_getx/modules/main/main_controller.dart'; +import 'package:news_getx/modules/widgets/button.dart'; +import 'package:news_getx/modules/widgets/input.dart'; +import 'package:news_getx/modules/widgets/toast.dart'; +import 'package:news_getx/theme/app_colors.dart'; + +/// 邮件订阅 +class NewsLetterWidget extends GetView { + @override + Widget build(BuildContext context) { + return Container( + padding: EdgeInsets.all(20.w), + child: Column( + children: [ + Row( + children: [ + Text( + 'Newsletter', + style: TextStyle( + fontFamily: 'Montserrat', + fontSize: 18.sp, + fontWeight: FontWeight.w300, + color: AppColors.thirdElementText, + ), + ), + Spacer(), + IconButton( + padding: EdgeInsets.zero, + alignment: Alignment.centerRight, + onPressed: () {}, + icon: Icon( + Icons.close, + color: AppColors.thirdElementText, + size: 18.sp, + ), + ) + ], + ), + // email + inputEmailEdit( + marginTop: 19, + keyboardType: TextInputType.emailAddress, + hintText: "Email", + isPassword: false, + controller: null, + ), + + // btn subcrible + Padding( + padding: EdgeInsets.only(top: 15), + child: FlatButton( + onPressed: () {}, + width: 335.w, + height: 44.h, + fontWeight: FontWeight.w600, + title: "Subscribe", + ), + ), + + // disc + Container( + margin: EdgeInsets.only(top: 29.h), + width: 261.w, + child: Text.rich( + TextSpan( + children: [ + TextSpan( + text: 'By clicking on Subscribe button you agree to accept', + style: TextStyle( + color: AppColors.thirdElementText, + fontFamily: "Avenir", + fontWeight: FontWeight.w400, + fontSize: 14.sp, + ), + ), + TextSpan( + text: ' Privacy Policy', + style: TextStyle( + color: AppColors.secondaryElementText, + fontFamily: "Avenir", + fontWeight: FontWeight.w400, + fontSize: 14.sp, + ), + recognizer: TapGestureRecognizer() + ..onTap = () { + toastInfo(msg: 'Privacy Policy'); + }, + ), + ], + ), + textAlign: TextAlign.center, + ), + ), + ], + ), + ); + } +} diff --git a/lib/modules/main/widgets/news_list.dart b/lib/modules/main/widgets/news_list.dart new file mode 100644 index 0000000..9c5ef4b --- /dev/null +++ b/lib/modules/main/widgets/news_list.dart @@ -0,0 +1,171 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:get/get.dart'; +import 'package:news_getx/data/model/news.dart'; +import 'package:news_getx/modules/main/main_controller.dart'; +import 'package:news_getx/modules/main/widgets/ad.dart'; +import 'package:news_getx/modules/widgets/image.dart'; +import 'package:news_getx/theme/app_colors.dart'; +import 'package:news_getx/utils/date.dart'; + +/// 新闻行 Item +class NewsListWidget extends GetView { + Widget _buildListItem(NewsItem item) { + return Container( + height: 161.h, + padding: EdgeInsets.all(20.w), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + // 左侧图 + InkWell( + onTap: () { + // 到详情页 + }, + child: SizedBox( + width: 121.w, + height: 121.w, + child: netImageCached( + item.thumbnail ?? "", + width: 121.w, + height: 121.w, + ), + ), + ), + + // 右侧内容 + SizedBox( + width: 194.w, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // 作者 + Container( + margin: EdgeInsets.all(0), + child: Text( + item.author ?? '', + style: TextStyle( + fontFamily: 'Avenir', + fontWeight: FontWeight.normal, + color: AppColors.thirdElementText, + fontSize: 14.sp, + ), + ), + ), + // 标题 + InkWell( + onTap: () { + // 到详情页 + }, + child: Container( + margin: EdgeInsets.only(top: 10.h), + child: Text( + item.title ?? '', + overflow: TextOverflow.clip, + maxLines: 3, + style: TextStyle( + fontFamily: 'Montserrat', + fontWeight: FontWeight.w500, + color: AppColors.primaryText, + fontSize: 16.sp, + ), + ), + ), + ), + + Spacer(), + // 一行 3 列 + Container( + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + ConstrainedBox( + // 分类 + constraints: BoxConstraints(maxWidth: 60.w), + child: Text( + item.category ?? "", + style: TextStyle( + fontFamily: 'Avenir', + fontWeight: FontWeight.normal, + color: AppColors.secondaryElementText, + fontSize: 14.sp, + height: 1, + ), + overflow: TextOverflow.clip, + maxLines: 1, + ), + ), + // 添加时间 + Container( + width: 15.w, + ), + ConstrainedBox( + constraints: BoxConstraints(maxWidth: 100.w), + child: Text( + '• ${timeLineFormat(item.addtime ?? DateTime(0))}', + style: TextStyle( + fontFamily: 'Avenir', + fontWeight: FontWeight.normal, + color: AppColors.thirdElementText, + fontSize: 14.sp, + height: 1, + ), + overflow: TextOverflow.clip, + maxLines: 1, + ), + ), + // 占满剩余空间 + Spacer(), + // 更多 + InkWell( + child: Icon( + Icons.more_horiz, + color: AppColors.primaryText, + size: 24, + ), + onTap: () { + print('查看更多...'); + }, + ), + ], + ), + ), ], + ), + ), + ], + ), + ); + } + + @override + Widget build(BuildContext context) { + // 这不是应该用ListView.build吗 + return Obx( + () => controller.state.newsPageList == null + ? Container() + : Column( + children: + controller.state.newsPageList!.items!.map((item) { + // 新闻行 + List widgets = [ + _buildListItem(item), + Divider(height: 1), + ]; + + // 每 5 条 显示广告 + int index = controller.state.newsPageList!.items!.indexOf(item); + if ((index + 1) % 5 == 0) { + widgets.addAll([ + AdWidget(), + Divider(height: 1), + ]); + } + + return Column( + children: widgets, + ); + }).toList(), + ), + ); + } +} diff --git a/lib/modules/main/widgets/recommend.dart b/lib/modules/main/widgets/recommend.dart index 77864b1..29ef1e7 100644 --- a/lib/modules/main/widgets/recommend.dart +++ b/lib/modules/main/widgets/recommend.dart @@ -9,7 +9,7 @@ import 'package:news_getx/utils/date.dart'; class NewsRecommendWidget extends GetView { @override Widget build(BuildContext context) { - print(controller.state.newsRecommend?.thumbnail); + print('推荐信息:${controller.state.newsRecommend?.thumbnail}'); return Obx( () => controller.state.newsRecommend == null ? Container() @@ -93,7 +93,9 @@ class NewsRecommendWidget extends GetView { color: AppColors.primaryText, size: 24, ), - onTap: () {}, + onTap: () { + print('查看更多...'); + }, ), ], ), diff --git a/test/widget_test.dart b/test/widget_test.dart deleted file mode 100644 index 28b1d6f..0000000 --- a/test/widget_test.dart +++ /dev/null @@ -1,30 +0,0 @@ -// This is a basic Flutter widget test. -// -// To perform an interaction with a widget in your test, use the WidgetTester -// utility in the flutter_test package. For example, you can send tap and scroll -// gestures. You can also use WidgetTester to find child widgets in the widget -// tree, read text, and verify that the values of widget properties are correct. - -import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; - -import 'package:news_getx/main.dart'; - -void main() { - testWidgets('Counter increments smoke test', (WidgetTester tester) async { - // Build our app and trigger a frame. - await tester.pumpWidget(const MyApp()); - - // Verify that our counter starts at 0. - expect(find.text('0'), findsOneWidget); - expect(find.text('1'), findsNothing); - - // Tap the '+' icon and trigger a frame. - await tester.tap(find.byIcon(Icons.add)); - await tester.pump(); - - // Verify that our counter has incremented. - expect(find.text('0'), findsNothing); - expect(find.text('1'), findsOneWidget); - }); -}