Flutter for OpenHarmony 第三方库实战:syncfusion_flutter_charts + connectivity 网络状态监控
想象一下这样的场景:用户打开你的应用,实时查看网络状态变化,同时以图表形式展示网络质量、流量使用等数据。这个流程涵盖了现代监控应用的核心体验。fill:#333;important;important;fill:none;color:#333;color:#333;important;fill:none;fill:#333;height:1em;网络状态监听数据采集图表可视化状态展示});});场

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net
🎯 欢迎来到 Flutter for OpenHarmony 第三方库实战系列!本文将带你构建一个完整的数据可视化仪表盘应用,通过组合
syncfusion_flutter_charts和connectivity_plus两个库,实现实时网络状态监控与数据可视化展示。
🚀 项目概述:我们要构建什么?
想象一下这样的场景:用户打开你的应用,实时查看网络状态变化,同时以图表形式展示网络质量、流量使用等数据。这个流程涵盖了现代监控应用的核心体验。
🎯 核心功能一览
| 功能模块 | 实现库 | 核心能力 |
|---|---|---|
| 📊 图表可视化 | syncfusion_flutter_charts | 折线图、柱状图、饼图等 |
| 📡 网络状态监控 | connectivity_plus | 实时网络状态检测与监听 |
💡 为什么选择这两个库?
1️⃣ syncfusion_flutter_charts - 企业级图表组件
- 支持 35+ 种图表类型
- 高性能渲染,支持大数据量
- 丰富的交互功能(缩放、平移、选择)
- 高度可定制的样式
2️⃣ connectivity_plus - 网络状态检测专家
- 实时监听网络状态变化
- 支持多种网络类型检测
- 跨平台支持,API 简洁
- 低性能消耗
📦 第一步:环境配置
1.1 添加依赖
打开 pubspec.yaml,添加两个库的依赖:
dependencies:
flutter:
sdk: flutter
# 图表组件
syncfusion_flutter_charts:
git:
url: "https://atomgit.com/openharmony-sig/fluttertpc_syncfusion_flutter_charts.git"
path: "packages/syncfusion_flutter_charts"
ref: br_syncfusion_flutter_charts-v29.1.33_ohos
# 网络状态检测
connectivity_plus:
git:
url: "https://atomgit.com/openharmony-sig/flutter_plus_plugins.git"
path: "packages/connectivity_plus/connectivity_plus"
1.2 权限配置
在 OpenHarmony 平台上,需要配置网络相关权限:
📄 ohos/entry/src/main/module.json5:
{
"module": {
"requestPermissions": [
{
"name": "ohos.permission.INTERNET",
"reason": "$string:internet_reason",
"usedScene": {
"abilities": ["EntryAbility"],
"when": "inuse"
}
},
{
"name": "ohos.permission.GET_NETWORK_INFO",
"reason": "$string:network_info_reason",
"usedScene": {
"abilities": ["EntryAbility"],
"when": "inuse"
}
}
]
}
}
📄 ohos/entry/src/main/resources/base/element/string.json:
{
"string": [
{
"name": "internet_reason",
"value": "应用需要访问网络以获取数据"
},
{
"name": "network_info_reason",
"value": "应用需要获取网络状态以监控连接"
}
]
}
1.3 执行依赖安装
flutter pub get
📡 第二步:网络状态监控模块
2.1 理解 Connectivity 的核心概念
connectivity_plus 提供了简单而强大的网络状态检测能力,通过 Stream 机制实现实时监听。
import 'package:connectivity_plus/connectivity_plus.dart';
// 获取当前网络状态
final result = await Connectivity().checkConnectivity();
// 监听网络状态变化
final subscription = Connectivity().onConnectivityChanged.listen((result) {
// 处理网络状态变化
});
2.2 网络状态服务封装
import 'dart:async';
import 'package:connectivity_plus/connectivity_plus.dart';
import 'package:flutter/foundation.dart';
enum NetworkStatus {
online,
offline,
unknown,
}
class NetworkService extends ChangeNotifier {
final Connectivity _connectivity = Connectivity();
StreamSubscription<ConnectivityResult>? _subscription;
NetworkStatus _status = NetworkStatus.unknown;
ConnectivityResult _connectionType = ConnectivityResult.none;
List<NetworkEvent> _history = [];
NetworkStatus get status => _status;
ConnectivityResult get connectionType => _connectionType;
List<NetworkEvent> get history => List.unmodifiable(_history);
bool get isOnline => _status == NetworkStatus.online;
bool get isOffline => _status == NetworkStatus.offline;
void startMonitoring() {
_checkInitialStatus();
_subscription = _connectivity.onConnectivityChanged.listen(_handleConnectivityChange);
}
void stopMonitoring() {
_subscription?.cancel();
_subscription = null;
}
Future<void> _checkInitialStatus() async {
try {
final result = await _connectivity.checkConnectivity();
_updateStatus(result);
} catch (e) {
debugPrint('检查网络状态失败: $e');
}
}
void _handleConnectivityChange(ConnectivityResult result) {
_updateStatus(result);
}
void _updateStatus(ConnectivityResult result) {
final previousType = _connectionType;
_connectionType = result;
_status = _determineStatus(result);
_history.add(NetworkEvent(
timestamp: DateTime.now(),
type: result,
status: _status,
));
if (_history.length > 100) {
_history.removeAt(0);
}
notifyListeners();
}
NetworkStatus _determineStatus(ConnectivityResult result) {
switch (result) {
case ConnectivityResult.wifi:
case ConnectivityResult.mobile:
case ConnectivityResult.ethernet:
return NetworkStatus.online;
case ConnectivityResult.none:
return NetworkStatus.offline;
default:
return NetworkStatus.unknown;
}
}
String get statusText {
switch (_status) {
case NetworkStatus.online:
return '网络已连接';
case NetworkStatus.offline:
return '网络已断开';
case NetworkStatus.unknown:
return '网络状态未知';
}
}
String get connectionTypeText {
switch (_connectionType) {
case ConnectivityResult.wifi:
return 'WiFi';
case ConnectivityResult.mobile:
return '移动网络';
case ConnectivityResult.ethernet:
return '以太网';
case ConnectivityResult.bluetooth:
return '蓝牙网络';
case ConnectivityResult.vpn:
return 'VPN';
case ConnectivityResult.none:
return '无连接';
default:
return '未知';
}
}
IconData get connectionIcon {
switch (_connectionType) {
case ConnectivityResult.wifi:
return Icons.wifi;
case ConnectivityResult.mobile:
return Icons.signal_cellular_4_bar;
case ConnectivityResult.ethernet:
return Icons.lan;
case ConnectivityResult.bluetooth:
return Icons.bluetooth;
case ConnectivityResult.vpn:
return Icons.vpn_lock;
case ConnectivityResult.none:
return Icons.signal_wifi_off;
default:
return Icons.help_outline;
}
}
Color get statusColor {
switch (_status) {
case NetworkStatus.online:
return Colors.green;
case NetworkStatus.offline:
return Colors.red;
case NetworkStatus.unknown:
return Colors.orange;
}
}
void dispose() {
stopMonitoring();
super.dispose();
}
}
class NetworkEvent {
final DateTime timestamp;
final ConnectivityResult type;
final NetworkStatus status;
NetworkEvent({
required this.timestamp,
required this.type,
required this.status,
});
}
📊 第三步:图表可视化模块
3.1 数据模型定义
class NetworkChartData {
final DateTime time;
final double value;
final String category;
NetworkChartData({
required this.time,
required this.value,
required this.category,
});
}
class NetworkTypeData {
final String type;
final double count;
final Color color;
NetworkTypeData({
required this.type,
required this.count,
required this.color,
});
}
3.2 实时折线图组件
import 'package:flutter/material.dart';
import 'package:syncfusion_flutter_charts/charts.dart';
class RealtimeLineChart extends StatelessWidget {
final List<NetworkChartData> data;
final String title;
final Color lineColor;
const RealtimeLineChart({
super.key,
required this.data,
required this.title,
this.lineColor = Colors.blue,
});
Widget build(BuildContext context) {
return Card(
elevation: 4,
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
title,
style: Theme.of(context).textTheme.titleMedium,
),
const SizedBox(height: 16),
SizedBox(
height: 200,
child: SfCartesianChart(
primaryXAxis: DateTimeAxis(
majorGridLines: const MajorGridLines(width: 0),
axisLine: const AxisLine(width: 0),
),
primaryYAxis: NumericAxis(
minimum: 0,
maximum: 100,
interval: 20,
axisLine: const AxisLine(width: 0),
majorTickLines: const MajorTickLines(size: 0),
),
tooltipBehavior: TooltipBehavior(
enable: true,
format: 'point.x: point.y',
),
series: <CartesianSeries<NetworkChartData, DateTime>>[
SplineAreaSeries<NetworkChartData, DateTime>(
dataSource: data,
xValueMapper: (datum, _) => datum.time,
yValueMapper: (datum, _) => datum.value,
gradient: LinearGradient(
colors: [
lineColor.withOpacity(0.4),
lineColor.withOpacity(0.0),
],
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
),
borderColor: lineColor,
borderWidth: 2,
),
],
),
),
],
),
),
);
}
}
3.3 网络类型分布饼图
class NetworkTypePieChart extends StatelessWidget {
final List<NetworkTypeData> data;
const NetworkTypePieChart({super.key, required this.data});
Widget build(BuildContext context) {
return Card(
elevation: 4,
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'网络类型分布',
style: Theme.of(context).textTheme.titleMedium,
),
const SizedBox(height: 16),
SizedBox(
height: 200,
child: SfCircularChart(
legend: Legend(
isVisible: true,
position: LegendPosition.right,
),
tooltipBehavior: TooltipBehavior(enable: true),
series: <CircularSeries>[
PieSeries<NetworkTypeData, String>(
dataSource: data,
xValueMapper: (datum, _) => datum.type,
yValueMapper: (datum, _) => datum.count,
pointColorMapper: (datum, _) => datum.color,
dataLabelSettings: const DataLabelSettings(
isVisible: true,
labelPosition: ChartDataLabelPosition.outside,
),
),
],
),
),
],
),
),
);
}
}
3.4 网络状态历史柱状图
class NetworkHistoryBarChart extends StatelessWidget {
final List<NetworkEvent> events;
const NetworkHistoryBarChart({super.key, required this.events});
List<_HourlyData> _processData() {
final now = DateTime.now();
final hourlyData = <int, int>{};
for (int i = 0; i < 24; i++) {
hourlyData[i] = 0;
}
for (final event in events) {
if (event.timestamp.day == now.day && event.status == NetworkStatus.online) {
final hour = event.timestamp.hour;
hourlyData[hour] = (hourlyData[hour] ?? 0) + 1;
}
}
return hourlyData.entries.map((e) => _HourlyData(e.key, e.value)).toList();
}
Widget build(BuildContext context) {
final data = _processData();
return Card(
elevation: 4,
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'今日连接次数',
style: Theme.of(context).textTheme.titleMedium,
),
const SizedBox(height: 16),
SizedBox(
height: 200,
child: SfCartesianChart(
primaryXAxis: CategoryAxis(
title: AxisTitle(text: '小时'),
majorGridLines: const MajorGridLines(width: 0),
),
primaryYAxis: NumericAxis(
title: AxisTitle(text: '次数'),
minimum: 0,
interval: 1,
),
series: <CartesianSeries<_HourlyData, String>>[
ColumnSeries<_HourlyData, String>(
dataSource: data,
xValueMapper: (datum, _) => '${datum.hour}时',
yValueMapper: (datum, _) => datum.count,
borderRadius: const BorderRadius.vertical(top: Radius.circular(4)),
color: Colors.blue,
),
],
),
),
],
),
),
);
}
}
class _HourlyData {
final int hour;
final int count;
_HourlyData(this.hour, this.count);
}
🔧 第四步:完整实战应用
4.1 主页面布局
import 'package:flutter/material.dart';
import 'package:connectivity_plus/connectivity_plus.dart';
import 'package:syncfusion_flutter_charts/charts.dart';
class NetworkDashboardPage extends StatefulWidget {
const NetworkDashboardPage({super.key});
State<NetworkDashboardPage> createState() => _NetworkDashboardPageState();
}
class _NetworkDashboardPageState extends State<NetworkDashboardPage> {
final NetworkService _networkService = NetworkService();
final List<NetworkChartData> _signalData = [];
final List<NetworkTypeData> _typeData = [];
void initState() {
super.initState();
_networkService.addListener(_onNetworkChanged);
_networkService.startMonitoring();
_initializeTypeData();
_startSignalSimulation();
}
void _initializeTypeData() {
_typeData.addAll([
NetworkTypeData(type: 'WiFi', count: 0, color: Colors.blue),
NetworkTypeData(type: '移动网络', count: 0, color: Colors.green),
NetworkTypeData(type: '以太网', count: 0, color: Colors.orange),
]);
}
void _startSignalSimulation() {
Future.delayed(const Duration(seconds: 1), () {
if (mounted) {
setState(() {
_signalData.add(NetworkChartData(
time: DateTime.now(),
value: _networkService.isOnline ? 70 + (DateTime.now().second % 30) : 0,
category: '信号强度',
));
if (_signalData.length > 30) {
_signalData.removeAt(0);
}
});
_startSignalSimulation();
}
});
}
void _onNetworkChanged() {
setState(() {
_updateTypeData();
});
}
void _updateTypeData() {
final type = _networkService.connectionType;
for (int i = 0; i < _typeData.length; i++) {
if ((type == ConnectivityResult.wifi && _typeData[i].type == 'WiFi') ||
(type == ConnectivityResult.mobile && _typeData[i].type == '移动网络') ||
(type == ConnectivityResult.ethernet && _typeData[i].type == '以太网')) {
_typeData[i] = NetworkTypeData(
type: _typeData[i].type,
count: _typeData[i].count + 1,
color: _typeData[i].color,
);
}
}
}
void dispose() {
_networkService.removeListener(_onNetworkChanged);
_networkService.dispose();
super.dispose();
}
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('网络监控仪表盘'),
elevation: 0,
),
body: ListView(
padding: const EdgeInsets.all(16),
children: [
_buildStatusCard(),
const SizedBox(height: 16),
RealtimeLineChart(
data: _signalData,
title: '信号强度实时监控',
lineColor: _networkService.statusColor,
),
const SizedBox(height: 16),
NetworkTypePieChart(data: _typeData),
const SizedBox(height: 16),
NetworkHistoryBarChart(events: _networkService.history),
const SizedBox(height: 16),
_buildEventLog(),
],
),
);
}
Widget _buildStatusCard() {
return Card(
elevation: 4,
child: Container(
padding: const EdgeInsets.all(20),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(12),
gradient: LinearGradient(
colors: [
_networkService.statusColor.withOpacity(0.1),
_networkService.statusColor.withOpacity(0.05),
],
),
),
child: Row(
children: [
Container(
width: 60,
height: 60,
decoration: BoxDecoration(
color: _networkService.statusColor.withOpacity(0.2),
shape: BoxShape.circle,
),
child: Icon(
_networkService.connectionIcon,
size: 32,
color: _networkService.statusColor,
),
),
const SizedBox(width: 20),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
_networkService.statusText,
style: Theme.of(context).textTheme.titleLarge?.copyWith(
fontWeight: FontWeight.bold,
color: _networkService.statusColor,
),
),
const SizedBox(height: 4),
Text(
'连接类型: ${_networkService.connectionTypeText}',
style: Theme.of(context).textTheme.bodyMedium,
),
],
),
),
Container(
width: 12,
height: 12,
decoration: BoxDecoration(
color: _networkService.statusColor,
shape: BoxShape.circle,
boxShadow: [
BoxShadow(
color: _networkService.statusColor.withOpacity(0.5),
blurRadius: 8,
spreadRadius: 2,
),
],
),
),
],
),
),
);
}
Widget _buildEventLog() {
final recentEvents = _networkService.history.reversed.take(10).toList();
return Card(
elevation: 4,
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'最近事件',
style: Theme.of(context).textTheme.titleMedium,
),
const SizedBox(height: 12),
if (recentEvents.isEmpty)
const Center(
child: Padding(
padding: EdgeInsets.all(20),
child: Text('暂无事件记录'),
),
)
else
Column(
children: recentEvents.map((event) {
return ListTile(
dense: true,
leading: Icon(
event.status == NetworkStatus.online
? Icons.check_circle
: Icons.cancel,
color: event.status == NetworkStatus.online
? Colors.green
: Colors.red,
),
title: Text(
event.status == NetworkStatus.online ? '网络已连接' : '网络已断开',
),
subtitle: Text(
'${event.timestamp.hour}:${event.timestamp.minute.toString().padLeft(2, '0')}:${event.timestamp.second.toString().padLeft(2, '0')}',
),
);
}).toList(),
),
],
),
),
);
}
}
📝 完整示例代码
以下是完整的 main.dart 示例代码,可以直接复制运行:
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:connectivity_plus/connectivity_plus.dart';
import 'package:syncfusion_flutter_charts/charts.dart';
void main() {
runApp(const NetworkDashboardApp());
}
class NetworkDashboardApp extends StatelessWidget {
const NetworkDashboardApp({super.key});
Widget build(BuildContext context) {
return MaterialApp(
title: '网络监控仪表盘',
debugShowCheckedModeBanner: false,
theme: ThemeData(
colorSchemeSeed: Colors.blue,
useMaterial3: true,
),
home: const NetworkDashboardPage(),
);
}
}
enum NetworkStatus { online, offline, unknown }
class NetworkEvent {
final DateTime timestamp;
final ConnectivityResult type;
final NetworkStatus status;
NetworkEvent({required this.timestamp, required this.type, required this.status});
}
class NetworkService extends ChangeNotifier {
final Connectivity _connectivity = Connectivity();
StreamSubscription<ConnectivityResult>? _subscription;
NetworkStatus _status = NetworkStatus.unknown;
ConnectivityResult _connectionType = ConnectivityResult.none;
final List<NetworkEvent> _history = [];
NetworkStatus get status => _status;
ConnectivityResult get connectionType => _connectionType;
List<NetworkEvent> get history => List.unmodifiable(_history);
bool get isOnline => _status == NetworkStatus.online;
void startMonitoring() {
_checkInitialStatus();
_subscription = _connectivity.onConnectivityChanged.listen(_handleConnectivityChange);
}
void stopMonitoring() {
_subscription?.cancel();
_subscription = null;
}
Future<void> _checkInitialStatus() async {
try {
final result = await _connectivity.checkConnectivity();
_updateStatus(result);
} catch (e) {
debugPrint('检查网络状态失败: $e');
}
}
void _handleConnectivityChange(ConnectivityResult result) {
_updateStatus(result);
}
void _updateStatus(ConnectivityResult result) {
_connectionType = result;
_status = _determineStatus(result);
_history.add(NetworkEvent(timestamp: DateTime.now(), type: result, status: _status));
if (_history.length > 100) _history.removeAt(0);
notifyListeners();
}
NetworkStatus _determineStatus(ConnectivityResult result) {
switch (result) {
case ConnectivityResult.wifi:
case ConnectivityResult.mobile:
case ConnectivityResult.ethernet:
return NetworkStatus.online;
case ConnectivityResult.none:
return NetworkStatus.offline;
default:
return NetworkStatus.unknown;
}
}
String get statusText {
switch (_status) {
case NetworkStatus.online: return '网络已连接';
case NetworkStatus.offline: return '网络已断开';
case NetworkStatus.unknown: return '网络状态未知';
}
}
String get connectionTypeText {
switch (_connectionType) {
case ConnectivityResult.wifi: return 'WiFi';
case ConnectivityResult.mobile: return '移动网络';
case ConnectivityResult.ethernet: return '以太网';
default: return '无连接';
}
}
IconData get connectionIcon {
switch (_connectionType) {
case ConnectivityResult.wifi: return Icons.wifi;
case ConnectivityResult.mobile: return Icons.signal_cellular_4_bar;
case ConnectivityResult.ethernet: return Icons.lan;
default: return Icons.signal_wifi_off;
}
}
Color get statusColor {
switch (_status) {
case NetworkStatus.online: return Colors.green;
case NetworkStatus.offline: return Colors.red;
case NetworkStatus.unknown: return Colors.orange;
}
}
void dispose() {
stopMonitoring();
super.dispose();
}
}
class NetworkChartData {
final DateTime time;
final double value;
NetworkChartData({required this.time, required this.value});
}
class NetworkTypeData {
final String type;
final double count;
final Color color;
NetworkTypeData({required this.type, required this.count, required this.color});
}
class NetworkDashboardPage extends StatefulWidget {
const NetworkDashboardPage({super.key});
State<NetworkDashboardPage> createState() => _NetworkDashboardPageState();
}
class _NetworkDashboardPageState extends State<NetworkDashboardPage> {
final NetworkService _networkService = NetworkService();
final List<NetworkChartData> _signalData = [];
final List<NetworkTypeData> _typeData = [];
Timer? _timer;
void initState() {
super.initState();
_networkService.addListener(_onNetworkChanged);
_networkService.startMonitoring();
_typeData.addAll([
NetworkTypeData(type: 'WiFi', count: 1, color: Colors.blue),
NetworkTypeData(type: '移动网络', count: 0, color: Colors.green),
NetworkTypeData(type: '以太网', count: 0, color: Colors.orange),
]);
_startSignalSimulation();
}
void _startSignalSimulation() {
_timer = Timer.periodic(const Duration(seconds: 1), (_) {
if (mounted) {
final now = DateTime.now();
final uniqueValue = 70.0 + (now.millisecond / 1000 * 30);
setState(() {
_signalData.add(NetworkChartData(
time: now,
value: _networkService.isOnline ? uniqueValue : 0.0,
));
if (_signalData.length > 30) _signalData.removeAt(0);
});
}
});
}
void _onNetworkChanged() => setState(() {});
void dispose() {
_timer?.cancel();
_networkService.removeListener(_onNetworkChanged);
_networkService.dispose();
super.dispose();
}
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('网络监控仪表盘'), elevation: 0),
body: ListView(
padding: const EdgeInsets.all(16),
children: [
_buildStatusCard(),
const SizedBox(height: 16),
_buildSignalChart(),
const SizedBox(height: 16),
_buildTypeChart(),
const SizedBox(height: 16),
_buildEventLog(),
],
),
);
}
Widget _buildStatusCard() {
return Card(
elevation: 4,
child: Container(
padding: const EdgeInsets.all(20),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(12),
gradient: LinearGradient(
colors: [_networkService.statusColor.withOpacity(0.1), _networkService.statusColor.withOpacity(0.05)],
),
),
child: Row(
children: [
Container(
width: 60, height: 60,
decoration: BoxDecoration(color: _networkService.statusColor.withOpacity(0.2), shape: BoxShape.circle),
child: Icon(_networkService.connectionIcon, size: 32, color: _networkService.statusColor),
),
const SizedBox(width: 20),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(_networkService.statusText, style: Theme.of(context).textTheme.titleLarge?.copyWith(fontWeight: FontWeight.bold, color: _networkService.statusColor)),
const SizedBox(height: 4),
Text('连接类型: ${_networkService.connectionTypeText}', style: Theme.of(context).textTheme.bodyMedium),
],
),
),
Container(width: 12, height: 12, decoration: BoxDecoration(color: _networkService.statusColor, shape: BoxShape.circle)),
],
),
),
);
}
Widget _buildSignalChart() {
return Card(
elevation: 4,
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('信号强度实时监控', style: Theme.of(context).textTheme.titleMedium),
const SizedBox(height: 16),
SizedBox(
height: 200,
child: SfCartesianChart(
primaryXAxis: DateTimeAxis(majorGridLines: const MajorGridLines(width: 0)),
primaryYAxis: NumericAxis(minimum: 0, maximum: 100, interval: 20),
series: <CartesianSeries<NetworkChartData, DateTime>>[
SplineAreaSeries<NetworkChartData, DateTime>(
dataSource: _signalData,
xValueMapper: (d, _) => d.time,
yValueMapper: (d, _) => d.value,
gradient: LinearGradient(colors: [_networkService.statusColor.withOpacity(0.4), _networkService.statusColor.withOpacity(0.0)], begin: Alignment.topCenter, end: Alignment.bottomCenter),
borderColor: _networkService.statusColor,
borderWidth: 2,
),
],
),
),
],
),
),
);
}
Widget _buildTypeChart() {
return Card(
elevation: 4,
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('网络类型分布', style: Theme.of(context).textTheme.titleMedium),
const SizedBox(height: 16),
SizedBox(
height: 200,
child: SfCircularChart(
legend: Legend(isVisible: true, position: LegendPosition.right),
series: <CircularSeries>[
PieSeries<NetworkTypeData, String>(
dataSource: _typeData,
xValueMapper: (d, _) => d.type,
yValueMapper: (d, _) => d.count,
pointColorMapper: (d, _) => d.color,
dataLabelSettings: const DataLabelSettings(isVisible: true),
),
],
),
),
],
),
),
);
}
Widget _buildEventLog() {
final recentEvents = _networkService.history.reversed.take(10).toList();
return Card(
elevation: 4,
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('最近事件', style: Theme.of(context).textTheme.titleMedium),
const SizedBox(height: 12),
if (recentEvents.isEmpty)
const Center(child: Padding(padding: EdgeInsets.all(20), child: Text('暂无事件记录')))
else
Column(
children: recentEvents.map((e) => ListTile(
dense: true,
leading: Icon(e.status == NetworkStatus.online ? Icons.check_circle : Icons.cancel, color: e.status == NetworkStatus.online ? Colors.green : Colors.red),
title: Text(e.status == NetworkStatus.online ? '网络已连接' : '网络已断开'),
subtitle: Text('${e.timestamp.hour}:${e.timestamp.minute.toString().padLeft(2, '0')}:${e.timestamp.second.toString().padLeft(2, '0')}'),
)).toList(),
),
],
),
),
);
}
}
🎯 最佳实践总结
5.1 网络监控最佳实践
| 场景 | 推荐做法 |
|---|---|
| 实时监控 | 使用 Stream 监听,避免轮询 |
| 状态缓存 | 保存历史状态,支持统计分析 |
| 错误处理 | 捕获异常,提供降级方案 |
| 资源释放 | 及时取消订阅,避免内存泄漏 |
5.2 图表使用最佳实践
| 场景 | 推荐图表类型 |
|---|---|
| 趋势展示 | 折线图、面积图 |
| 对比分析 | 柱状图、条形图 |
| 占比分布 | 饼图、环形图 |
| 实时数据 | 动态更新的折线图 |
📌 参考资源:
更多推荐



所有评论(0)