网络文章-v1
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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(),
|
||||
);
|
||||
|
||||
@@ -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),
|
||||
)
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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<AppNavigation> {
|
||||
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<AppNavigation> {
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
controller: _ctrl,
|
||||
children: const [
|
||||
GuessPage(),
|
||||
MuyuPage(),
|
||||
Paper(),
|
||||
GuessPage(),
|
||||
MuyuPage(),
|
||||
Paper(),
|
||||
NetArticlePage(),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
23
lib/net_article/api/article_api.dart
Normal file
23
lib/net_article/api/article_api.dart
Normal file
@@ -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<List<Article>> 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 [];
|
||||
}
|
||||
}
|
||||
24
lib/net_article/model/article.dart
Normal file
24
lib/net_article/model/article.dart
Normal file
@@ -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}';
|
||||
}
|
||||
}
|
||||
114
lib/net_article/views/article_content.dart
Normal file
114
lib/net_article/views/article_content.dart
Normal file
@@ -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<ArticleContent> createState() => _ArticleContentState();
|
||||
}
|
||||
|
||||
class _ArticleContentState extends State<ArticleContent> {
|
||||
List<Article> _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<Article> 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,
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
32
lib/net_article/views/article_detail_page.dart
Normal file
32
lib/net_article/views/article_detail_page.dart
Normal file
@@ -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<ArticleDetailPage> createState() => _ArticleDetailPageState();
|
||||
}
|
||||
|
||||
class _ArticleDetailPageState extends State<ArticleDetailPage> {
|
||||
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),
|
||||
);
|
||||
}
|
||||
}
|
||||
18
lib/net_article/views/net_articel_page.dart
Normal file
18
lib/net_article/views/net_articel_page.dart
Normal file
@@ -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(),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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))
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
40
pubspec.lock
40
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:
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user