Rebuild the Flutter/DJI-MSDK-V4 Fly App to the v2 UI kit and wire the full SDK surface behind it. Native (Kotlin): split DjiSdkBridge into a method/event router delegating to per-subsystem SubBridge helpers sharing a BridgeCtx — FlightController (takeoff/land/RTH + rich telemetry), Camera (mode/record/photo/exposure), Gimbal, Mission (Waypoint + ActiveTrack; QuickShots via ActiveTrack QUICK_SHOT), Media (MediaManager list/thumbnail/download), and optional DJI account login. Manifest gains scoped media permissions. Flutter: ten screens under lib/ui/ (Flight HUD, capture modes, camera settings, settings menu, map+waypoints, home, album, academy, profile, routes/flight logs), driven by an expanded FlightModel. New PVIcon renders the kit's SVG paths via flutter_svg; map uses flutter_map + latlong2. Pin transitive androidx.core/browser down to SDK-35-compatible versions so the newer plugins don't force AGP 8.9.1 onto the DJI toolchain. Verified with `flutter build apk --debug` (compiles Dart + all Kotlin); runtime behaviour is untested here — it needs a physical DJI-connected device. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
132 lines
7.3 KiB
Dart
132 lines
7.3 KiB
Dart
import 'package:flutter/material.dart';
|
||
import 'package:flutter_svg/flutter_svg.dart';
|
||
|
||
/// The v2 UI-kit line-icon library.
|
||
///
|
||
/// Ported verbatim from the `P = {…}` SVG-path map in
|
||
/// `Design/PilotVault Project logo/ui_kits/fly/index-v2.html`. Every icon is a
|
||
/// single `d` attribute drawn on a 24×24 grid with round caps/joins, matching
|
||
/// the kit's `<Icon>` component. Rendered through [PVIcon].
|
||
class PvPaths {
|
||
PvPaths._();
|
||
|
||
static const Map<String, String> d = <String, String>{
|
||
// nav / system
|
||
'chevronLeft': 'M15 6l-6 6 6 6',
|
||
'chevronRight': 'M9 6l6 6-6 6',
|
||
'chevronDown': 'M6 9l6 6 6-6',
|
||
'close': 'M6 6l12 12M18 6L6 18',
|
||
'more': 'M5 12h.01M12 12h.01M19 12h.01',
|
||
'home': 'M3 10.5 12 3l9 7.5M5 9.5V21h14V9.5',
|
||
'settings':
|
||
'M12 15a3 3 0 1 0 0-6 3 3 0 0 0 0 6z M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 1 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 1 1-2.83-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 1 1 2.83-2.83l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 1 1 2.83 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z',
|
||
// flight status
|
||
'satellite':
|
||
'M13 7 9 3 5 7l4 4M17 11l4 4-4 4-4-4M8.5 8.5 3 14l7 7 5.5-5.5M16 16l1-1M18 3a3 3 0 0 1 3 3',
|
||
'battery': 'M3 8h14a2 2 0 0 1 2 2v4a2 2 0 0 1-2 2H3a1 1 0 0 1-1-1V9a1 1 0 0 1 1-1zM22 11v2',
|
||
'radio':
|
||
'M4.9 19.1a10 10 0 0 1 0-14.2M7.8 16.2a6 6 0 0 1 0-8.4M16.2 7.8a6 6 0 0 1 0 8.4M19.1 4.9a10 10 0 0 1 0 14.2M12 13a1 1 0 1 0 0-2 1 1 0 0 0 0 2z',
|
||
'rc': 'M7 8h10a3 3 0 0 1 3 3v6a3 3 0 0 1-3 3H7a3 3 0 0 1-3-3v-6a3 3 0 0 1 3-3zM8 4v4M16 4v4M9 14h2M8 13v2M15 13h.01M17 15h.01',
|
||
'obstacle': 'M12 3 2 20h20L12 3zM12 10v4M12 17h.01',
|
||
'compass': 'M12 21a9 9 0 1 0 0-18 9 9 0 0 0 0 18zM15.5 8.5l-2 5-5 2 2-5 5-2z',
|
||
'gimbal': 'M12 3a9 9 0 1 0 0 18 9 9 0 0 0 0-18zM12 8v8M8 12h8',
|
||
'crosshair': 'M12 21a9 9 0 1 0 0-18 9 9 0 0 0 0 18zM22 12h-4M6 12H2M12 6V2M12 22v-4',
|
||
'drone':
|
||
'M6 6l3.5 3.5M18 6l-3.5 3.5M6 18l3.5-3.5M18 18l-3.5-3.5M6 6a2.5 2.5 0 1 0-.01-.01M18 6a2.5 2.5 0 1 0-.01 0M6 18a2.5 2.5 0 1 0-.01 0M18 18a2.5 2.5 0 1 0-.01 0M9.5 9.5h5v5h-5z',
|
||
'takeoff': 'M12 20V8m0 0-4 4m4-4 4 4M4 4h16',
|
||
'rth': 'M12 3a9 9 0 1 0 9 9M12 3v6l4 2M20 4l-3 1 1-3',
|
||
'map': 'M9 4 3 6v14l6-2 6 2 6-2V4l-6 2-6-2zM9 4v14M15 6v14',
|
||
'pin': 'M12 21s7-6.4 7-12A7 7 0 0 0 5 9c0 5.6 7 12 7 12zM12 11a2 2 0 1 0 0-4 2 2 0 0 0 0 4z',
|
||
'route': 'M6 19a3 3 0 1 0 0-6 3 3 0 0 0 0 6zM18 11a3 3 0 1 0 0-6 3 3 0 0 0 0 6zM6 13V9a4 4 0 0 1 4-4h4',
|
||
// camera
|
||
'video': 'M23 7l-7 5 7 5V7zM3 5h11a2 2 0 0 1 2 2v10a2 2 0 0 1-2 2H3a2 2 0 0 1-2-2V7a2 2 0 0 1 2-2z',
|
||
'image': 'M3 3h18v18H3zM8.5 10a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3zM21 15l-5-5L5 21',
|
||
'aperture':
|
||
'M12 21a9 9 0 1 0 0-18 9 9 0 0 0 0 18zM14.3 8 20 8M12.6 4.5 9.8 9.3M7 6 10 11.2M9.7 16 4 16M11.4 19.5l2.8-4.8M17 18l-3-5.2',
|
||
'iso': 'M4 7v10M8 7c-2 0-2 5 0 5s2 5 0 5M14 7a3 5 0 0 1 0 10 3 5 0 0 1 0-10M19 7v10',
|
||
'shutter': 'M12 21a9 9 0 1 0 0-18 9 9 0 0 0 0 18zM12 12l6-3M12 12l-3 6M12 12l-3-6M12 12l6 3',
|
||
'ev': 'M4 6h10M4 12h7M4 18h10M17 9v6M20 12h-6',
|
||
'wb': 'M12 17a5 5 0 1 0 0-10 5 5 0 0 0 0 10zM12 2v3M12 19v3M5 12H2M22 12h-3',
|
||
'timer': 'M12 22a8 8 0 1 0 0-16 8 8 0 0 0 0 16zM12 10v4l2 2M9 2h6',
|
||
'burst': 'M7 8h10v10H7zM5 6h10M5 10v6',
|
||
'film': 'M3 3h18v18H3zM7 3v18M17 3v18M3 8h4M3 16h4M17 8h4M17 16h4',
|
||
'pano': 'M2 7l20-3v16L2 17V7zM2 7v10',
|
||
'grid': 'M3 3h18v18H3zM3 9h18M3 15h18M9 3v18M15 3v18',
|
||
'histogram': 'M4 20V10M9 20V4M14 20v-8M19 20v-6',
|
||
'focus':
|
||
'M3 8V5a2 2 0 0 1 2-2h3M16 3h3a2 2 0 0 1 2 2v3M21 16v3a2 2 0 0 1-2 2h-3M8 21H5a2 2 0 0 1-2-2v-3M12 15a3 3 0 1 0 0-6 3 3 0 0 0 0 6z',
|
||
'slowmo': 'M12 21a9 9 0 1 0 0-18 9 9 0 0 0 0 18zM10 9v6l5-3z',
|
||
'hyperlapse': 'M12 8v4l3 2M4 12a8 8 0 1 1 3 6M4 20v-4h4',
|
||
'master': 'M12 3l2.6 5.3 5.9.9-4.3 4.1 1 5.8-5.2-2.7-5.2 2.7 1-5.8L3.5 9.2l5.9-.9L12 3z',
|
||
'hdr': 'M4 8v8M4 12h4M8 8v8M12 8v8h2a3 3 0 0 0 0-6M12 12h2M18 8v8M18 12h2l1 4',
|
||
// quickshots
|
||
'dronie': 'M12 4a3 3 0 1 0 0 6 3 3 0 0 0 0-6zM4 20l8-8 8 8',
|
||
'rocket': 'M12 3c4 2 5 7 5 10l-2 3H9l-2-3c0-3 1-8 5-10zM12 9v.01M8 17l-3 4M16 17l3 4',
|
||
'circle': 'M12 21a9 9 0 1 0 0-18 9 9 0 0 0 0 18zM12 8a4 4 0 1 0 0 8 4 4 0 0 0 0-8z',
|
||
'helix': 'M6 3c12 3-12 15 0 18M6 6c9 2.2-9 11.6 0 13.5',
|
||
'boomerang': 'M4 20C4 10 10 4 20 4c0 10-6 16-16 16zM4 20l6-6',
|
||
'asteroid': 'M12 21a9 9 0 1 0 0-18 9 9 0 0 0 0 18zM3 12h18M8 4c-2 2.5-2 13.5 0 16M16 4c2 2.5 2 13.5 0 16',
|
||
// home / library / misc
|
||
'academy': 'M22 10 12 5 2 10l10 5 10-5zM6 12v5c0 1 2.7 2.5 6 2.5s6-1.5 6-2.5v-5',
|
||
'album': 'M3 3h18v18H3zM8.5 10a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3zM21 15l-5-5L5 21',
|
||
'gauge': 'M12 15a3 3 0 1 0 0-6M3.5 18a9 9 0 1 1 17 0',
|
||
'user': 'M20 21a8 8 0 1 0-16 0M12 11a4 4 0 1 0 0-8 4 4 0 0 0 0 8z',
|
||
'sdcard': 'M18 2H8L4 6v14a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V4a2 2 0 0 0-2-2zM10 6v2M13 6v2M16 6v2',
|
||
'link': 'M10 13a5 5 0 0 0 7 0l3-3a5 5 0 0 0-7-7l-1 1M14 11a5 5 0 0 0-7 0l-3 3a5 5 0 0 0 7 7l1-1',
|
||
'play': 'M8 5v14l11-7z',
|
||
'pause': 'M8 5v14M16 5v14',
|
||
'sun': 'M12 17a5 5 0 1 0 0-10 5 5 0 0 0 0 10zM12 1v2M12 21v2M4.2 4.2l1.4 1.4M18.4 18.4l1.4 1.4M1 12h2M21 12h2M4.2 19.8l1.4-1.4M18.4 5.6l1.4-1.4',
|
||
'bell': 'M18 8a6 6 0 0 0-12 0c0 7-3 9-3 9h18s-3-2-3-9M13.73 21a2 2 0 0 1-3.46 0',
|
||
'share': 'M4 12v8a1 1 0 0 0 1 1h14a1 1 0 0 0 1-1v-8M16 6l-4-4-4 4M12 2v13',
|
||
'search': 'M11 19a8 8 0 1 0 0-16 8 8 0 0 0 0 16zM21 21l-4.3-4.3',
|
||
'check': 'M20 6 9 17l-5-5',
|
||
'shield': 'M12 3l8 3v6c0 5-3.5 8-8 9-4.5-1-8-4-8-9V6l8-3z',
|
||
'download': 'M12 3v12m0 0-4-4m4 4 4-4M4 21h16',
|
||
'play2': 'M8 5v14l11-7z',
|
||
};
|
||
}
|
||
|
||
/// A single v2 line icon. Strokes one of [PvPaths.d] on a 24×24 grid, round
|
||
/// caps/joins, matching the design's `<Icon>` component.
|
||
class PVIcon extends StatelessWidget {
|
||
const PVIcon(
|
||
this.name, {
|
||
super.key,
|
||
this.size = 20,
|
||
this.color = Colors.white,
|
||
this.stroke = 2,
|
||
this.fill = false,
|
||
});
|
||
|
||
final String name;
|
||
final double size;
|
||
final Color color;
|
||
final double stroke;
|
||
|
||
/// When true (e.g. play triangles), the path is filled with [color] and drawn
|
||
/// with no stroke — matches the kit's `fill="#fff" stroke="#fff"` usage.
|
||
final bool fill;
|
||
|
||
@override
|
||
Widget build(BuildContext context) {
|
||
final String d = PvPaths.d[name] ?? '';
|
||
final String hex = _hex(color);
|
||
final String fillAttr = fill ? hex : 'none';
|
||
final String strokeAttr = fill ? 'none' : hex;
|
||
final String svg =
|
||
'<svg xmlns="http://www.w3.org/2000/svg" width="$size" height="$size" '
|
||
'viewBox="0 0 24 24" fill="$fillAttr" stroke="$strokeAttr" '
|
||
'stroke-width="$stroke" stroke-linecap="round" stroke-linejoin="round">'
|
||
'<path d="$d"/></svg>';
|
||
return SvgPicture.string(svg, width: size, height: size);
|
||
}
|
||
|
||
static String _hex(Color c) {
|
||
final int r = (c.r * 255).round();
|
||
final int g = (c.g * 255).round();
|
||
final int b = (c.b * 255).round();
|
||
String two(int v) => v.toRadixString(16).padLeft(2, '0');
|
||
return '#${two(r)}${two(g)}${two(b)}';
|
||
}
|
||
}
|