gnss_rtk 1.0.0 copy "gnss_rtk: ^1.0.0" to clipboard
gnss_rtk: ^1.0.0 copied to clipboard

PlatformAndroid

Production-ready GNSS RTK positioning library for industrial applications with NMEA parsing and multi-provider support

example/lib/main.dart

// example/lib/main.dart
// Example application demonstrating GNSS RTK library usage

import 'package:flutter/material.dart';
import 'package:gnss_rtk/gnss_rtk.dart';
import 'dart:async';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'GNSS RTK Example',
      theme: ThemeData(
        primarySwatch: Colors.blue,
        useMaterial3: true,
      ),
      home: const GnssRtkDemo(),
    );
  }
}

class GnssRtkDemo extends StatefulWidget {
  const GnssRtkDemo({Key? key}) : super(key: key);

  @override
  State<GnssRtkDemo> createState() => _GnssRtkDemoState();
}

class _GnssRtkDemoState extends State<GnssRtkDemo> {
  GnssService? _gnssService;
  GnssState? _currentState;
  GnssFix? _currentFix;
  List<SatelliteInfo> _satellites = [];
  GnssDiagnostics? _diagnostics;
  
  StreamSubscription? _stateSubscription;
  StreamSubscription? _positionSubscription;
  StreamSubscription? _satellitesSubscription;
  
  @override
  void initState() {
    super.initState();
    _initializeGnss();
  }

  Future<void> _initializeGnss() async {
    // Create mock provider for demonstration
    // In production, use TcpGnssProvider, BroadcastGnssProvider, etc.
    final provider = MockGnssProvider(
      const MockGnssProviderConfig(
        mockScenario: MockScenario.stationary,
        updateInterval: Duration(seconds: 1),
      ),
    );

    _gnssService = GnssService(
      provider: provider,
      enableHealthMonitoring: true,
    );

    // Subscribe to streams
    _stateSubscription = _gnssService!.stateStream.listen((state) {
      setState(() {
        _currentState = state;
      });
    });

    _positionSubscription = _gnssService!.positionStream.listen((fix) {
      setState(() {
        _currentFix = fix;
      });
    });

    _satellitesSubscription = _gnssService!.satellitesStream.listen((satellites) {
      setState(() {
        _satellites = satellites;
      });
    });

    // Start periodic diagnostics update
    Timer.periodic(const Duration(seconds: 5), (_) {
      if (mounted) {
        setState(() {
          _diagnostics = _gnssService?.getDiagnostics();
        });
      }
    });
  }

  Future<void> _startService() async {
    try {
      await _gnssService?.start();
      _showSnackBar('GNSS service started', Colors.green);
    } catch (e) {
      _showSnackBar('Failed to start: $e', Colors.red);
    }
  }

  Future<void> _stopService() async {
    try {
      await _gnssService?.stop();
      _showSnackBar('GNSS service stopped', Colors.orange);
    } catch (e) {
      _showSnackBar('Failed to stop: $e', Colors.red);
    }
  }

  void _showSnackBar(String message, Color color) {
    ScaffoldMessenger.of(context).showSnackBar(
      SnackBar(
        content: Text(message),
        backgroundColor: color,
        duration: const Duration(seconds: 2),
      ),
    );
  }

  @override
  void dispose() {
    _stateSubscription?.cancel();
    _positionSubscription?.cancel();
    _satellitesSubscription?.cancel();
    _gnssService?.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('GNSS RTK Example'),
        elevation: 2,
      ),
      body: SingleChildScrollView(
        padding: const EdgeInsets.all(16.0),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.stretch,
          children: [
            // Connection Status Card
            _buildStatusCard(),
            const SizedBox(height: 16),
            
            // Position Card
            _buildPositionCard(),
            const SizedBox(height: 16),
            
            // Satellites Card
            _buildSatellitesCard(),
            const SizedBox(height: 16),
            
            // Diagnostics Card
            _buildDiagnosticsCard(),
            const SizedBox(height: 16),
            
            // Control Buttons
            _buildControlButtons(),
          ],
        ),
      ),
    );
  }

  Widget _buildStatusCard() {
    final state = _currentState;
    final isConnected = state?.connectionState.isConnected ?? false;
    
    return Card(
      child: Padding(
        padding: const EdgeInsets.all(16.0),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Row(
              children: [
                Icon(
                  isConnected ? Icons.check_circle : Icons.cancel,
                  color: isConnected ? Colors.green : Colors.red,
                ),
                const SizedBox(width: 8),
                Text(
                  'Connection Status',
                  style: Theme.of(context).textTheme.titleLarge,
                ),
              ],
            ),
            const Divider(),
            _buildInfoRow('State', state?.connectionState.name ?? 'Unknown'),
            _buildInfoRow('Provider', state?.providerName ?? '-'),
            if (state?.errorMessage != null)
              _buildInfoRow('Error', state!.errorMessage!, isError: true),
          ],
        ),
      ),
    );
  }

  Widget _buildPositionCard() {
    final fix = _currentFix;
    
    return Card(
      child: Padding(
        padding: const EdgeInsets.all(16.0),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Text(
              'Position Fix',
              style: Theme.of(context).textTheme.titleLarge,
            ),
            const Divider(),
            if (fix != null) ...[
              _buildInfoRow('Latitude', fix.latitude.toStringAsFixed(8)),
              _buildInfoRow('Longitude', fix.longitude.toStringAsFixed(8)),
              if (fix.altitude != null)
                _buildInfoRow('Altitude', '${fix.altitude!.toStringAsFixed(2)} m'),
              _buildInfoRow('Fix Quality', fix.fixQuality.description),
              _buildInfoRow('Fix Mode', fix.fixMode.description),
              if (fix.estimatedAccuracy != null)
                _buildInfoRow('Accuracy', '${fix.estimatedAccuracy!.toStringAsFixed(2)} m'),
              if (fix.satelliteCount != null)
                _buildInfoRow('Satellites', fix.satelliteCount.toString()),
              if (fix.hdop != null)
                _buildInfoRow('HDOP', fix.hdop!.toStringAsFixed(2)),
            ] else
              const Text('No position fix available'),
          ],
        ),
      ),
    );
  }

  Widget _buildSatellitesCard() {
    return Card(
      child: Padding(
        padding: const EdgeInsets.all(16.0),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Text(
              'Satellites (${_satellites.length})',
              style: Theme.of(context).textTheme.titleLarge,
            ),
            const Divider(),
            if (_satellites.isNotEmpty)
              SizedBox(
                height: 120,
                child: ListView.builder(
                  scrollDirection: Axis.horizontal,
                  itemCount: _satellites.length,
                  itemBuilder: (context, index) {
                    final sat = _satellites[index];
                    return _buildSatelliteCard(sat);
                  },
                ),
              )
            else
              const Text('No satellite data'),
          ],
        ),
      ),
    );
  }

  Widget _buildSatelliteCard(SatelliteInfo sat) {
    final hasSignal = sat.snr != null && sat.snr! > 0;
    
    return Card(
      color: sat.isUsedInFix ? Colors.green.shade50 : null,
      child: Padding(
        padding: const EdgeInsets.all(8.0),
        child: Column(
          mainAxisSize: MainAxisSize.min,
          children: [
            Text(
              '${sat.constellation.name.toUpperCase()}-${sat.prn}',
              style: const TextStyle(fontWeight: FontWeight.bold),
            ),
            const SizedBox(height: 4),
            if (sat.snr != null) ...[
              Text('SNR: ${sat.snr} dB'),
              SizedBox(
                width: 40,
                child: LinearProgressIndicator(
                  value: (sat.snr! / 50).clamp(0.0, 1.0),
                  backgroundColor: Colors.grey.shade300,
                  valueColor: AlwaysStoppedAnimation<Color>(
                    hasSignal ? Colors.green : Colors.red,
                  ),
                ),
              ),
            ],
          ],
        ),
      ),
    );
  }

  Widget _buildDiagnosticsCard() {
    final diag = _diagnostics;
    
    return Card(
      child: Padding(
        padding: const EdgeInsets.all(16.0),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Text(
              'System Health',
              style: Theme.of(context).textTheme.titleLarge,
            ),
            const Divider(),
            if (diag != null) ...[
              _buildInfoRow('Status', diag.overallHealth.name),
              _buildInfoRow('Signal', '${diag.signalStrength ?? 0}%'),
              _buildInfoRow('Fix Stability', '${diag.fixStabilityScore}%'),
              if (diag.issues.isNotEmpty) ...[
                const SizedBox(height: 8),
                const Text('Issues:', style: TextStyle(fontWeight: FontWeight.bold)),
                ...diag.issues.map((issue) => Padding(
                  padding: const EdgeInsets.only(left: 8.0, top: 4.0),
                  child: Text(
                    '• ${issue.message}',
                    style: TextStyle(
                      color: issue.severity == HealthStatus.critical 
                          ? Colors.red 
                          : Colors.orange,
                    ),
                  ),
                )),
              ],
            ] else
              const Text('Diagnostics not available'),
          ],
        ),
      ),
    );
  }

  Widget _buildControlButtons() {
    final isRunning = _gnssService?.isRunning ?? false;
    
    return Row(
      children: [
        Expanded(
          child: ElevatedButton.icon(
            onPressed: isRunning ? null : _startService,
            icon: const Icon(Icons.play_arrow),
            label: const Text('Start'),
          ),
        ),
        const SizedBox(width: 16),
        Expanded(
          child: ElevatedButton.icon(
            onPressed: isRunning ? _stopService : null,
            icon: const Icon(Icons.stop),
            label: const Text('Stop'),
            style: ElevatedButton.styleFrom(
              backgroundColor: Colors.red,
              foregroundColor: Colors.white,
            ),
          ),
        ),
      ],
    );
  }

  Widget _buildInfoRow(String label, String value, {bool isError = false}) {
    return Padding(
      padding: const EdgeInsets.symmetric(vertical: 4.0),
      child: Row(
        mainAxisAlignment: MainAxisAlignment.spaceBetween,
        children: [
          Text(label, style: const TextStyle(fontWeight: FontWeight.w500)),
          Text(
            value,
            style: TextStyle(
              color: isError ? Colors.red : null,
            ),
          ),
        ],
      ),
    );
  }
}
0
likes
150
points
33
downloads

Documentation

Documentation
API reference

Publisher

unverified uploader

Weekly Downloads

Production-ready GNSS RTK positioning library for industrial applications with NMEA parsing and multi-provider support

Homepage
Repository
View/report issues

Topics

#gnss #rtk #nmea #gps #geospatial

License

MIT (license)

Dependencies

collection, flutter, logging, meta, rxdart

More

Packages that depend on gnss_rtk

Packages that implement gnss_rtk