diff --git a/android/app/build.gradle b/android/app/build.gradle index 0aa49e5..4bd0c70 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -47,7 +47,7 @@ android { applicationId "com.toly1994.flutter_first_station" // You can update the following values to match your application needs. // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration. - minSdkVersion flutter.minSdkVersion + minSdkVersion 19 targetSdkVersion flutter.targetSdkVersion versionCode flutterVersionCode.toInteger() versionName flutterVersionName diff --git a/lib/guess/guess_app_bar.dart b/lib/guess/guess_app_bar.dart index 9c45c12..04e0dbb 100644 --- a/lib/guess/guess_app_bar.dart +++ b/lib/guess/guess_app_bar.dart @@ -14,14 +14,7 @@ class GuessAppBar extends StatelessWidget implements PreferredSizeWidget { @override Widget build(BuildContext context) { return AppBar( - systemOverlayStyle: const SystemUiOverlayStyle( - statusBarIconBrightness: Brightness.dark, - statusBarColor: Colors.transparent), - titleSpacing: 0, - leading: Icon( - Icons.menu, - color: Colors.black, - ), + leading: Icon(Icons.menu, color: Colors.black), actions: [ IconButton( splashRadius: 20, @@ -31,8 +24,6 @@ class GuessAppBar extends StatelessWidget implements PreferredSizeWidget { color: Colors.blue, )) ], - backgroundColor: Colors.white, - elevation: 0, title: TextField( controller: controller, keyboardType: TextInputType.number, diff --git a/lib/main.dart b/lib/main.dart index d33f1ad..fd61512 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'counter/counter_page.dart'; import 'guess/guess_page.dart'; @@ -23,6 +24,17 @@ class MyApp extends StatelessWidget { debugShowCheckedModeBanner: false, theme: ThemeData( primarySwatch: Colors.blue, + appBarTheme: const AppBarTheme( + elevation: 0, + centerTitle: true, + systemOverlayStyle: SystemUiOverlayStyle( + statusBarIconBrightness: Brightness.dark, + statusBarColor: Colors.transparent, + ), + backgroundColor: Colors.white, + titleTextStyle: TextStyle(color: Colors.black, fontSize: 16, fontWeight: FontWeight.bold), + iconTheme: IconThemeData(color: Colors.black), + ) ), home: const AppNavigation(), ); diff --git a/lib/muyu/muyu_app_bar.dart b/lib/muyu/muyu_app_bar.dart index 3787d7e..f89f9f3 100644 --- a/lib/muyu/muyu_app_bar.dart +++ b/lib/muyu/muyu_app_bar.dart @@ -12,18 +12,12 @@ class MuyuAppBar extends StatelessWidget implements PreferredSizeWidget { @override Widget build(BuildContext context) { return AppBar( - elevation: 0, - centerTitle: true, - systemOverlayStyle: const SystemUiOverlayStyle( - statusBarIconBrightness: Brightness.dark, - statusBarColor: Colors.transparent), - backgroundColor: Colors.white, - titleTextStyle: const TextStyle( - color: Colors.black, fontSize: 16, fontWeight: FontWeight.bold), - iconTheme: const IconThemeData(color: Colors.black), title: const Text("电子木鱼"), actions: [ - IconButton(onPressed: onTapHistory, icon: const Icon(Icons.history)) + IconButton( + onPressed: onTapHistory, + icon: const Icon(Icons.history), + ) ], ); } diff --git a/lib/navigation/app_navigation.dart b/lib/navigation/app_navigation.dart index 5f29c2f..15d8eac 100644 --- a/lib/navigation/app_navigation.dart +++ b/lib/navigation/app_navigation.dart @@ -3,6 +3,7 @@ import 'package:flutter_first_station/guess/guess_page.dart'; import 'package:flutter_first_station/muyu/muyu_page.dart'; import 'package:flutter_first_station/paper/paper.dart'; +import '../net_article/views/net_articel_page.dart'; import 'app_bottom_bar.dart'; class AppNavigation extends StatefulWidget { @@ -20,21 +21,18 @@ class _AppNavigationState extends State { MenuData(label: '猜数字', icon: Icons.question_mark), MenuData(label: '电子木鱼', icon: Icons.my_library_music_outlined), MenuData(label: '白板绘制', icon: Icons.palette_outlined), + MenuData(label: '网络文章', icon: Icons.article_outlined), ]; @override Widget build(BuildContext context) { - return Column( - children: [ - Expanded( - child: _buildContent(), - ), - AppBottomBar( - currentIndex: _index, - onItemTap: _onChangePage, - menus: menus, - ) - ], + return Scaffold( + body: _buildContent(), + bottomNavigationBar: AppBottomBar( + currentIndex: _index, + onItemTap: _onChangePage, + menus: menus, + ), ); } @@ -50,11 +48,11 @@ class _AppNavigationState extends State { physics: const NeverScrollableScrollPhysics(), controller: _ctrl, children: const [ - GuessPage(), - MuyuPage(), - Paper(), + GuessPage(), + MuyuPage(), + Paper(), + NetArticlePage(), ], ); } } - diff --git a/lib/net_article/api/article_api.dart b/lib/net_article/api/article_api.dart new file mode 100644 index 0000000..2838ba9 --- /dev/null +++ b/lib/net_article/api/article_api.dart @@ -0,0 +1,23 @@ + +import 'package:dio/dio.dart'; + +import '../model/article.dart'; + +class ArticleApi{ + + static const String kBaseUrl = 'https://www.wanandroid.com'; + + final Dio _client = Dio(BaseOptions(baseUrl: kBaseUrl)); + + Future> loadArticles(int page) async { + String path = '/article/list/$page/json'; + var rep = await _client.get(path); + if (rep.statusCode == 200) { + if(rep.data!=null){ + var data = rep.data['data']['datas'] as List; + return data.map(Article.formMap).toList(); + } + } + return []; + } +} \ No newline at end of file diff --git a/lib/net_article/model/article.dart b/lib/net_article/model/article.dart new file mode 100644 index 0000000..a120d97 --- /dev/null +++ b/lib/net_article/model/article.dart @@ -0,0 +1,24 @@ +class Article { + final String title; + final String url; + final String time; + + const Article({ + required this.title, + required this.time, + required this.url, + }); + + factory Article.formMap(dynamic json) { + return Article( + title: json['title'] ?? '未知', + url: json['link'] ?? '', + time: json['niceDate'] ?? '', + ); + } + + @override + String toString() { + return 'Article{title: $title, url: $url, time: $time}'; + } +} diff --git a/lib/net_article/views/article_content.dart b/lib/net_article/views/article_content.dart new file mode 100644 index 0000000..136d7bb --- /dev/null +++ b/lib/net_article/views/article_content.dart @@ -0,0 +1,114 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_first_station/net_article/api/article_api.dart'; +import '../model/article.dart'; +import 'article_detail_page.dart'; + +class ArticleContent extends StatefulWidget { + const ArticleContent({Key? key}) : super(key: key); + + @override + State createState() => _ArticleContentState(); +} + +class _ArticleContentState extends State { + List
_articles = []; + ArticleApi api = ArticleApi(); + + @override + void initState() { + super.initState(); + _loadData(); + } + + void _loadData() async { + _articles = await api.loadArticles(0); + setState(() {}); + } + + @override + Widget build(BuildContext context) { + return ListView.builder( + itemExtent: 80, + itemCount: _articles.length, + itemBuilder: _buildItemByIndex, + ); + } + + Widget _buildItemByIndex(BuildContext context, int index) { + return ArticleItem( + article: _articles[index], + onTap: _jumpToPage, + ); + } + + void _jumpToPage(Article article) { + Navigator.of(context).push( + MaterialPageRoute( + builder: (_) => ArticleDetailPage(article: article), + ), + ); + } +} + +class ArticleItem extends StatelessWidget { + final Article article; + final ValueChanged
onTap; + + const ArticleItem({Key? key, required this.article, required this.onTap}) + : super(key: key); + + @override + Widget build(BuildContext context) { + return GestureDetector( + onTap: () => onTap(article), + child: Card( + elevation: 0, + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(6)), + color: Colors.white, + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 12.0, vertical: 8), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + Expanded( + child: Text( + article.title, + maxLines: 1, + overflow: TextOverflow.ellipsis, + style: + TextStyle(fontSize: 14, fontWeight: FontWeight.bold), + ), + ), + const SizedBox( + width: 10, + ), + // Spacer(), + Text( + article.time, + style: TextStyle(fontSize: 12, color: Colors.grey), + ), + ], + ), + const SizedBox( + height: 4, + ), + Expanded( + child: Align( + alignment: Alignment.centerLeft, + child: Text( + article.url, + style: TextStyle(fontSize: 12, color: Colors.grey), + maxLines: 2, + overflow: TextOverflow.ellipsis, + ), + ), + ) + ], + ), + ), + ), + ); + } +} diff --git a/lib/net_article/views/article_detail_page.dart b/lib/net_article/views/article_detail_page.dart new file mode 100644 index 0000000..283b431 --- /dev/null +++ b/lib/net_article/views/article_detail_page.dart @@ -0,0 +1,32 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_first_station/net_article/model/article.dart'; +import 'package:webview_flutter/webview_flutter.dart'; + +class ArticleDetailPage extends StatefulWidget { + final Article article; + + const ArticleDetailPage({Key? key, required this.article}) : super(key: key); + + @override + State createState() => _ArticleDetailPageState(); +} + +class _ArticleDetailPageState extends State { + late WebViewController controller; + @override + void initState() { + super.initState(); + controller = WebViewController() + ..setJavaScriptMode(JavaScriptMode.unrestricted) + ..setBackgroundColor(const Color(0x00000000)) + ..loadRequest(Uri.parse(widget.article.url)); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar(title: Text(widget.article.title)), + body: WebViewWidget(controller: controller), + ); + } +} diff --git a/lib/net_article/views/net_articel_page.dart b/lib/net_article/views/net_articel_page.dart new file mode 100644 index 0000000..415fac1 --- /dev/null +++ b/lib/net_article/views/net_articel_page.dart @@ -0,0 +1,18 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; + +import 'article_content.dart'; + +class NetArticlePage extends StatelessWidget { + const NetArticlePage({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text('网络请求测试'), + ), + body: ArticleContent(), + ); + } +} diff --git a/lib/paper/paper_app_bar.dart b/lib/paper/paper_app_bar.dart index 92e6cfb..e7c136e 100644 --- a/lib/paper/paper_app_bar.dart +++ b/lib/paper/paper_app_bar.dart @@ -17,30 +17,17 @@ class PaperAppBar extends StatelessWidget implements PreferredSizeWidget { @override Widget build(BuildContext context) { return AppBar( - centerTitle: true, - elevation: 0, - systemOverlayStyle: const SystemUiOverlayStyle( - statusBarIconBrightness: Brightness.dark, - statusBarColor: Colors.transparent), - backgroundColor: Colors.white, leading: BackUpButtons( onBack: onBack, onRevocation: onRevocation, ), leadingWidth: 100, - title: Text( - '画板绘制', - style: TextStyle(color: Colors.black, fontSize: 16), - ), + title: Text('画板绘制'), actions: [ IconButton( splashRadius: 20, onPressed: onClear, - icon: Icon( - CupertinoIcons.delete, - color: Colors.black, - size: 20, - )) + icon: Icon(CupertinoIcons.delete, size: 20)) ], ); } diff --git a/pubspec.lock b/pubspec.lock index 9f2ad41..b442063 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -113,6 +113,14 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "1.0.5" + dio: + dependency: "direct main" + description: + name: dio + sha256: "347d56c26d63519552ef9a569f2a593dda99a81fdbdff13c584b7197cfe05059" + url: "https://pub.flutter-io.cn" + source: hosted + version: "5.1.2" fake_async: dependency: transitive description: @@ -485,6 +493,38 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "2.1.4" + webview_flutter: + dependency: "direct main" + description: + name: webview_flutter + sha256: "1a37bdbaaf5fbe09ad8579ab09ecfd473284ce482f900b5aea27cf834386a567" + url: "https://pub.flutter-io.cn" + source: hosted + version: "4.2.0" + webview_flutter_android: + dependency: transitive + description: + name: webview_flutter_android + sha256: "1acea8def62592123e2fbbca164ed8681a98a890bdcbb88f916d5b4a22687759" + url: "https://pub.flutter-io.cn" + source: hosted + version: "3.7.0" + webview_flutter_platform_interface: + dependency: transitive + description: + name: webview_flutter_platform_interface + sha256: "78715dc442b7849dbde74e92bb67de1cecf5addf95531c5fb474e72f5fe9a507" + url: "https://pub.flutter-io.cn" + source: hosted + version: "2.3.0" + webview_flutter_wkwebview: + dependency: transitive + description: + name: webview_flutter_wkwebview + sha256: "4646bb68297803bdbb96d46853e8fcb560d6cb5e04153fa64581535767875dfe" + url: "https://pub.flutter-io.cn" + source: hosted + version: "3.4.3" win32: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index b262a66..9787050 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -35,6 +35,8 @@ dependencies: intl: ^0.18.1 shared_preferences: ^2.1.1 sqflite: ^2.2.8+2 + dio: ^5.1.2 + webview_flutter: ^4.2.0 # The following adds the Cupertino Icons font to your application. # Use with the CupertinoIcons class for iOS style icons. cupertino_icons: ^1.0.2