在这里插入图片描述

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net

🎯 欢迎来到 Flutter for OpenHarmony 第三方库实战系列!本文将带你构建一个完整的数据可视化仪表盘应用,通过组合 syncfusion_flutter_chartsconnectivity_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 图表使用最佳实践

场景 推荐图表类型
趋势展示 折线图、面积图
对比分析 柱状图、条形图
占比分布 饼图、环形图
实时数据 动态更新的折线图

📌 参考资源

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐