Flutter 前端请求 API 接口渲染空白页问题排查指南
可能是因为前端请求的 API 接口返回的数据格式与前端解析数据的方式不匹配导致的空白页。建议检查一下后端返回的数据格式和前端解析数据的方式是否一致,并查看网络请求是否成功。同时,可以在前端代码中添加一些错误处理逻辑,如显示错误信息等,便于定位问题。
以下是前端代码示例,其中使用了 'dart:convert'、'package:flutter/material.dart'、'package:http/http.dart' 和 'package:dio/dio.dart' 库:
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'package:dio/dio.dart';
//以卡片形式展示
void main() {
runApp(Myapp());
}
class Myapp extends StatefulWidget {
const Myapp({Key? key}) : super(key: key);
@override
State<Myapp> createState() => _MyappState();
}
class _MyappState extends State<Myapp> {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'test',
home: Scaffold(body: FundsList()),
);
}
}
class Fund {
String id;
String name;
String type;
String? d1;
String? d2;
String? d3;
String? d4;
String? d5;
String? d1_r;
String? d2_r;
String? d3_r;
String? d4_r;
String? d5_r;
Fund({
required this.id,
required this.name,
required this.type,
this.d1,
this.d2,
this.d3,
this.d4,
this.d5,
this.d1_r,
this.d2_r,
this.d3_r,
this.d4_r,
this.d5_r,
});
factory Fund.fromJson(Map<String, dynamic> json) {
return Fund(
id: json['id'],
name: json['name'],
type: json['type'],
d1: json['d1'],
d2: json['d2'],
d3: json['d3'],
d4: json['d4'],
d5: json['d5'],
d1_r: json['d1_r'],
d2_r: json['d2_r'],
d3_r: json['d3_r'],
d4_r: json['d4_r'],
d5_r: json['d5_r'],
);
}
}
class FundsList extends StatefulWidget {
@override
_FundsListState createState() => _FundsListState();
}
String addLineBreaks(String text) {
if (text == null) {
return '';
}
var formatted = text.replaceAll(',', ',
');
return formatted;
}
TextSpan buildColoredTextSpan(String text, Color positiveColor, Color negativeColor,
{bool shouldBreakLine = true, bool colorText = true}) {
if (text == null) {
text = 'null';
}
var number = double.tryParse(text);
var isPositive = number != null && number > 0;
var style = TextStyle(color: isPositive ? positiveColor : negativeColor);
if (!colorText) {
style = TextStyle(color: Colors.black);
}
var formatted = shouldBreakLine ? addLineBreaks(text) : text;
return TextSpan(
text: formatted,
style: style,
);
}
class _FundsListState extends State<FundsList> {
List<Fund> _funds = [];
bool _isLoadingMore = false;
int _currentPage = 1;
ScrollController _scrollController = ScrollController();
Future<void> _getFunds() async {
try {
Response response = await Dio().get('http://172.21.74.112:5000/funds?page=1&per_page=10');
print(response.data);
print(response.data['funds']);
List<dynamic> data = response.data['funds'];
setState(() {
_funds = data.map((fund) => Fund.fromJson(fund)).toList();
});
} catch (e) {
print(e);
}
}
Future<void> _loadMoreFunds() async {
try {
_isLoadingMore = true;
_currentPage++;
Response response = await Dio()
.get('http://172.21.74.112:5000/funds?page=$_currentPage&per_page=10');
List<dynamic> data = response.data['funds'];
setState(() {
_funds += data.map((fund) => Fund.fromJson(fund)).toList();
});
} catch (e) {
print(e);
} finally {
_isLoadingMore = false;
}
}
@override
void initState() {
super.initState();
_getFunds();
_scrollController.addListener(() {
if (_scrollController.position.pixels ==
_scrollController.position.maxScrollExtent) {
_loadMoreFunds();
}
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Funds List'),
),
body: ListView.builder(
controller: _scrollController,
itemCount: _funds.length + (_isLoadingMore ? 1 : 0),
itemBuilder: (BuildContext context, int index) {
if (index == _funds.length && _isLoadingMore) {
return Center(
child: CircularProgressIndicator(),
);
} else if (index == _funds.length) {
return Container();
}
else {
return Card(
margin: EdgeInsets.symmetric(vertical: 8.0, horizontal: 16.0),
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text('基金代码:'+_funds[index].id),
Text(_funds[index].name),
Text(_funds[index].type),
RichText(
text: TextSpan(
children: [
TextSpan(text: '第一天:', style: TextStyle(color: Colors.black)),
buildColoredTextSpan('${_funds[index].d1 ?? 'null'}' ,Colors.red, Colors.green,shouldBreakLine: false, colorText: true),
TextSpan(text: '第二天:', style: TextStyle(color: Colors.black)),
buildColoredTextSpan('${_funds[index].d2 ?? 'null'}', Colors.red, Colors.green, shouldBreakLine: false, colorText: true),
TextSpan(text: '第三天:', style: TextStyle(color: Colors.black)),
buildColoredTextSpan('${_funds[index].d3 ?? 'null'}', Colors.red, Colors.green, shouldBreakLine: false, colorText: true),
TextSpan(text: '第四天:', style: TextStyle(color: Colors.black)),
buildColoredTextSpan('${_funds[index].d4 ?? 'null'}', Colors.red, Colors.green, shouldBreakLine: false, colorText: true),
TextSpan(text: '
第五天:', style: TextStyle(color: Colors.black)),
buildColoredTextSpan('${_funds[index].d5 ?? 'null'}', Colors.red, Colors.green, shouldBreakLine: false, colorText: true),
TextSpan(text: '
第一天排名:${_funds[index].d1_r ?? 'null'}', style: TextStyle(color: Colors.black)),
TextSpan(text: '第二天排名:${_funds[index].d2_r ?? 'null'}', style: TextStyle(color: Colors.black)),
TextSpan(text: '第三天排名:${_funds[index].d3_r ?? 'null'}', style: TextStyle(color: Colors.black)),
TextSpan(text: '第四天排名:${_funds[index].d4_r ?? 'null'}', style: TextStyle(color: Colors.black)),
TextSpan(text: '第五天排名:${_funds[index].d5_r ?? 'null'}', style: TextStyle(color: Colors.black)),
],
),
),
//下面是之前用的,没有判断正负号来决定颜色
// Text('第二天:'+(_funds[index].d2 ?? 'null')),
// Text('第三天:'+(_funds[index].d3 ?? 'null')),
// Text('第四天:'+(_funds[index].d4 ?? 'null')),
// Text('第五天:'+(_funds[index].d5 ?? 'null')),
SizedBox(height: 16.0), // 添加间隔
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
ElevatedButton(
style: ElevatedButton.styleFrom(
primary: Colors.green, // 按钮背景颜色
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8.0), // 设置按钮圆角
),
),
onPressed: () {
print('Selected: ${_funds[index].name} - ${_funds[index].type} 打印自选');
},
child: Text('自选'),
),
ElevatedButton(
style: ElevatedButton.styleFrom(
primary: Colors.red, // 按钮背景颜色
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8.0), // 设置按钮圆角
),
),
onPressed: () {
print('Selected: ${_funds[index].name} - ${_funds[index].type} 打印买入');
},
child: Text('买入'),
),
ElevatedButton(
style: ElevatedButton.styleFrom(
primary: Colors.blue, // 按钮背景颜色
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8.0), // 设置按钮圆角
),
),
onPressed: () {
print('Selected: ${_funds[index].name} - ${_funds[index].type} 打印打印');
},
child: Text('打印'),
),
],
),
],
),
),
);
}
},
),
);
}
}
以下是后端代码示例,使用了 'sqlalchemy' 和 'flask' 库,并通过 'flask_sqlalchemy' 连接数据库:
import sqlalchemy
from flask import Flask, jsonify, request
from flask_sqlalchemy import SQLAlchemy
#成功实现分页并展示基金
app = Flask(__name__)
app.config['JSON_AS_ASCII'] = False
app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql://root:3454123@localhost:3306/fund' # 设置数据库URI
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
db = SQLAlchemy(app)
class Fund(db.Model):
__tablename__='funds'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(255))
type = db.Column(db.String(255))
d1 =db.Column(sqlalchemy.types.DECIMAL(precision=10, scale=2))
d2 =db.Column(sqlalchemy.types.DECIMAL(precision=10, scale=2))
d3 =db.Column(sqlalchemy.types.DECIMAL(precision=10, scale=2))
d4 =db.Column(sqlalchemy.types.DECIMAL(precision=10, scale=2))
d5 =db.Column(sqlalchemy.types.DECIMAL(precision=10, scale=2))
d1_r=db.Column(db.Integer)
d2_r=db.Column(db.Integer)
d3_r=db.Column(db.Integer)
d4_r=db.Column(db.Integer)
d5_r=db.Column(db.Integer)
@app.route('/funds', methods=['GET'])
#下面的get_users代码普通展示基金表,没有按照单日排名什么的
# def get_users():
# page = int(request.args.get('page', 1))
# per_page = int(request.args.get('per_page', 10))
# print(page,per_page)
# funds = Fund.query.paginate(page=page, per_page=per_page, error_out=False)
# result = {
# 'funds': [{'id': fund.id, 'name': fund.name, 'type': fund.type,
# 'd1':fund.d1,'d2':fund.d2,'d3':fund.d3,'d4':fund.d4,'d5':fund.d5,
# 'd1_r':str(fund.d1_r),'d2_r':str(fund.d2_r),'d3_r':str(fund.d3_r),'d4_r':str(fund.d4_r),'d5_r':str(fund.d5_r)
# } for fund in funds.items],
# 'total_pages': funds.pages,
# 'total_users': funds.total,
# 'current_page': funds.page,
# 'per_page': funds.per_page,
# 'has_prev': funds.has_prev,
# 'has_next': funds.has_next
# }
# return jsonify(result)
#以单日排名来排序的基金数据
def get_users():
page = int(request.args.get('page', 1))
per_page = int(request.args.get('per_page', 10))
print(page, per_page)
# 获取基金数据
funds = Fund.query.paginate(page=page, per_page=per_page, error_out=False)
# 计算每只基金的五日涨跌幅之和和平均涨跌幅
for fund in funds.items:
d_list = [fund.d1, fund.d2, fund.d3, fund.d4, fund.d5]
total_d = sum(d_list)
avg_d = total_d / 5
setattr(fund, 'total_d', total_d)
setattr(fund, 'avg_d', avg_d)
# 对基金进行排序
sorted_funds = sorted(funds.items, key=lambda x: x.total_d, reverse=True)
result = {
'funds': [{'id': fund.id,
'name': fund.name,
'type': fund.type,
'd1': fund.d1,
'd2': fund.d2,
'd3': fund.d3,
'd4': fund.d4,
'd5': fund.d5,
'd1_r': str(fund.d1_r),
'd2_r': str(fund.d2_r),
'd3_r': str(fund.d3_r),
'd4_r': str(fund.d4_r),
'd5_r': str(fund.d5_r),
'total_d': fund.total_d,
'avg_d': fund.avg_d
} for fund in sorted_funds],
'total_pages': funds.pages,
'total_users': funds.total,
'current_page': funds.page,
'per_page': funds.per_page,
'has_prev': funds.has_prev,
'has_next': funds.has_next
}
return jsonify(result)
if __name__ == '__main__':
app.run(host='0.0.0.0',debug=True)
请确保前端代码中的 Fund.fromJson 方法与后端返回的数据格式相匹配。
如果仍然无法解决问题,可以尝试以下步骤:
- 使用浏览器开发者工具中的 Network 标签查看网络请求是否成功,以及后端返回的具体数据格式。
- 在前端代码中添加错误处理逻辑,例如捕获网络请求异常,并显示错误信息。
- 使用调试工具,例如 Flutter DevTools,进行代码调试,排查问题所在。
希望这些信息能帮助您解决问题。
原文地址: https://www.cveoy.top/t/topic/nwR4 著作权归作者所有。请勿转载和采集!