簡介
聽詩吧 是一個集 詩詞/詩人推薦、搜尋、簡介、賞析、朗讀(下個版本) 於一體的詩詞興趣 APP。涵蓋了從古代到近代的 20,000 位詩人的 500,000 首詩詞作品,敘事詩、抒情詩、送別詩、邊塞詩、山水田園詩、懷古詩、悼亡詩,詠物詩 應有盡有?。
- APP 端基於 Google 最新研發的 Flutter UI 框架,一套程式碼庫高效構建多平臺精美應用(移動、Web、桌面和嵌入式平臺)?,配合 MaterialDesign 的設計風格 和 卡片式佈局 讓你眼前一亮。更有微信分享功能,好東西當然要分享?~
專案地址: github.com/mmtou/liste…
下載體驗:
github.com/mmtou/liste…
- API 端基於 SpringBoot 微服務架構 和 輕量級的 MySQL 資料庫,給你帶來高效、穩定的服務體驗。更整合了百度的語音合成技術,讓你暢快的享受詩詞帶來的樂趣?。
先睹為快
app端專案結構
├── components // 元件
│ ├── avatar.dart // 頭像元件,前期根據名稱生成對應顏色的頭像
│ ├── empty.dart // 置空元件
│ ├── poet.dart // 詩人列表
│ ├── poet_item.dart // 單個詩人
│ ├── poetry.dart // 詩詞列表
│ ├── poetry_item.dart // 單個詩詞
│ └── recommend.dart // 推薦頁元件
├── main.dart
├── utils // 工具類
│ ├── common.dart // 通用工具
│ ├── constant.dart // 常量
│ ├── favorite.dart // 點愛心的管理
│ ├── http.dart // 網路請求
└── views // 頁面
├── feedback.dart // 反饋
├── index.dart // 首頁
├── poet_detail.dart // 詩人
├── poetry_detail.dart // 詩詞
└── search.dart // 搜尋
複製程式碼
核心程式碼
- APP 啟動時從本地讀取點贊列表
void main() {
if (Platform.isAndroid) {
SystemUiOverlayStyle systemUiOverlayStyle =
SystemUiOverlayStyle(statusBarColor: Colors.transparent);
SystemChrome.setSystemUIOverlayStyle(systemUiOverlayStyle);
}
WidgetsFlutterBinding.ensureInitialized();
run();
}
void run() async {
await Favorite.getInstance();
runApp(MyApp());
}
...
class Favorite {
...
static Future<bool> getInstance() async {
prefs = await SharedPreferences.getInstance();
favorites = prefs.getStringList('favorites') ?? [];
return true;
}
...
}
複製程式碼
- 頭像元件,根據詩人名稱,計算16位UTF-16程式碼單元,然後根據規定的顏色陣列長度取餘,得到index,從顏色陣列的中得到某個色值,即為頭像的背景色。
Widget build(BuildContext context) {
String name = Common.getShortName(authorName);
int index = name.codeUnitAt(0) % Constant.avatarColors.length;
Color color = Constant.avatarColors[index];
return InkWell(
onTap: () {
if (authorId != null) {
Common.toPoetDetail(context, authorId);
}
},
child: CircleAvatar(
backgroundColor: color,
child: Center(
child: Text(
name,
style: TextStyle(
fontWeight: FontWeight.w600,
color: Colors.white,
),
),
),
),
);
}
複製程式碼
- 微信分享,整合 fluwx 第三方外掛,快速實現微信分享、登入、支付等功能。
- 建立 keystore,修改相應配置
- 檢視簽名,執行
keytool -list -v -keystore key.keystore
命令後的MD5去掉:轉為小寫即為微信開放平臺的簽名。 - 登入微信開放平臺建立應用,填寫包名、簽名等資訊
fluwx.registerWxApi( appId: "xxxx", universalLink: "xxxx"); fluwx.shareToWeChat(WeChatShareTextModel( text: content, transaction: "transaction}", scene: WeChatScene.SESSION)); 複製程式碼
請移步 flutterchina.club/android-rel… 檢視簽名配置
http.dart
單例模式,減少資源浪費
import 'package:bot_toast/bot_toast.dart';
import 'package:dio/dio.dart';
import './constant.dart';
class Http {
Dio _dio;
// 工廠模式
factory Http() => _getInstance();
static Http get instance => _getInstance();
static Http _instance;
Http._internal() {
// 初始化
if (_dio == null) {
_dio = Dio(
BaseOptions(
baseUrl: Constant.host,
connectTimeout: 10000,
receiveTimeout: 6000,
headers: {'Content-Type': 'application/json'}),
);
_dio.interceptors
.add(InterceptorsWrapper(onRequest: (RequestOptions options) async {
BotToast.showLoading();
return options; //continue
}, onResponse: (Response response) async {
BotToast.closeAllLoading();
var data = response.data;
if (!data['success']) {
BotToast.showText(text: data['message']);
throw (data['message']);
}
return response.data['result']; // continue
}, onError: (DioError e) async {
BotToast.closeAllLoading();
if ('DioErrorType.DEFAULT' == e.type.toString()) {
BotToast.showText(text: e.error);
} else {
BotToast.showText(text: '伺服器異常');
}
// Do something with response error
return e; //continue
}));
}
}
static Http _getInstance() {
if (_instance == null) {
_instance = Http._internal();
}
return _instance;
}
Future get(uri, {queryParameters}) async {
try {
Response response = await _dio.get(uri, queryParameters: queryParameters);
print(response);
return response.data;
} catch (e) {
print(e);
}
}
Future post(uri, {json}) async {
try {
Response response = await _dio.post(uri, data: json);
return response.data;
} catch (e) {
print(e);
}
}
}
複製程式碼
- 使用
TabView
時,避免頁面重繪,造成資源浪費和體驗下降;子元件整合AutomaticKeepAliveClientMixin
實現快取策略,且在build
中加入super.build(context);
class Poetry extends StatefulWidget {
@override
_PoetryState createState() => _PoetryState();
}
class _PoetryState extends State<Poetry> with AutomaticKeepAliveClientMixin {
List list;
int pageNum = 1;
@override
Widget build(BuildContext context) {
super.build(context);
return EasyRefresh(
header:
BezierCircleHeader(backgroundColor: Theme.of(context).primaryColor),
footer:
BezierBounceFooter(backgroundColor: Theme.of(context).primaryColor),
onRefresh: () => refresh(),
onLoad: () => onLoad(),
child: this.list == null
? Empty('暫無資料~')
: ListView.separated(
padding: EdgeInsets.all(16),
itemCount: this.list.length,
separatorBuilder: (BuildContext context, int index) => Container(
height: 12,
),
itemBuilder: (BuildContext context, int index) {
var item = this.list[index];
return PoetryItem(item);
}),
);
}
refresh() async {
this.pageNum = 1;
this.list = [];
await this.getList();
return true;
}
onLoad() async {
pageNum += 1;
await this.getList();
return true;
}
// 詩詞列表
getList() async {
var data = await Http().get('/poetry/list?pageNum=$pageNum');
if (data != null) {
List list = data['list'];
this.list = this.list ?? [];
setState(() {
this.list.addAll(list);
});
}
}
@override
void initState() {
super.initState();
this.getList();
}
@override
bool get wantKeepAlive => true;
複製程式碼
InkWell
元件可以實現點選後的波紋反饋效果,但是InkWell
的child不能設定背景色,不然會覆蓋掉波紋的效果。需要你又需要背景色怎麼辦呢?可以在InkWell
上包一層Ink
。
Ink(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(8),
),
child: InkWell(
onTap: () => Common.toPoetryDetail(context, widget.poetry),
child: Container(
padding: EdgeInsets.all(12),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8),
),
),
)
);
複製程式碼
功能
- 推薦
- 廣場
- 詩人
- 詩詞詳情
- 詩人詳情
- 微信分享
- 搜尋
- 詩詞朗讀
- 登入
最後
未完待續~
願你走出半生,歸來仍是少年。