diff --git a/flutter_app/.metadata b/flutter_app/.metadata index eb35243..dc16213 100644 --- a/flutter_app/.metadata +++ b/flutter_app/.metadata @@ -4,6 +4,7 @@ # This file should be version controlled and should not be manually edited. version: + revision: "2663184aa79047d0a33a14a3b607954f8fdd8730" revision: "2663184aa79047d0a33a14a3b607954f8fdd8730" channel: "stable" diff --git a/flutter_app/lib/main.dart b/flutter_app/lib/main.dart index 878e68d..06ddaf0 100644 --- a/flutter_app/lib/main.dart +++ b/flutter_app/lib/main.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:imacs/screens/home_screen.dart'; +import 'package:imacs/widgets/nav_bar_widget.dart'; void main() async { runApp(const App()); @@ -17,6 +18,12 @@ class App extends StatelessWidget { ), routes: { '/': (BuildContext context) => HomePage(title: 'WARG IMACS'), + '/logs': (BuildContext context) => + const PlaceholderScreen(title: 'Logs'), + '/camera': (BuildContext context) => + const PlaceholderScreen(title: 'Camera'), + '/sitl': (BuildContext context) => + const PlaceholderScreen(title: 'SITL'), }, ); } diff --git a/flutter_app/lib/screens/home_screen.dart b/flutter_app/lib/screens/home_screen.dart index f06b1e7..832dddb 100644 --- a/flutter_app/lib/screens/home_screen.dart +++ b/flutter_app/lib/screens/home_screen.dart @@ -3,6 +3,7 @@ import 'package:imacs/defaults.dart'; import 'package:imacs/modules/mavlink_communication.dart'; import 'package:imacs/modules/get_drone_information.dart'; import 'package:imacs/widgets/drone_information_widget.dart'; +import 'package:imacs/widgets/nav_bar_widget.dart'; class HomePage extends StatelessWidget { HomePage({Key? key, required this.title}) : super(key: key); @@ -15,16 +16,16 @@ class HomePage extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( - appBar: AppBar( - title: Text(title), - ), - body: Column( - children: [ - DroneInformation( - getDroneInformation: GetDroneInformation(comm: comm), - ), - ], - ), - ); + appBar: AppBar( + title: Text(title), + ), + body: Column( + children: [ + DroneInformation( + getDroneInformation: GetDroneInformation(comm: comm), + ), + ], + ), + bottomNavigationBar: const NavBar()); } } diff --git a/flutter_app/lib/widgets/nav_bar_widget.dart b/flutter_app/lib/widgets/nav_bar_widget.dart new file mode 100644 index 0000000..6e42d93 --- /dev/null +++ b/flutter_app/lib/widgets/nav_bar_widget.dart @@ -0,0 +1,74 @@ +import 'package:flutter/material.dart'; + +/// Widget for navigating between different screens +/// +/// This widget displays the different sceeens in tabs along a bar at +/// the bottom of the screen. When clicked, each tab will navigate to +/// the corresponding screen. +class NavBar extends StatelessWidget { + /// @brief Constructs a NavBar widget. + const NavBar({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return BottomNavigationBar( + onTap: (int index) { + switch (index) { + case 0: + Navigator.pushReplacementNamed(context, '/'); + break; + case 1: + Navigator.popAndPushNamed(context, '/logs'); + break; + case 2: + Navigator.popAndPushNamed(context, '/camera'); + break; + default: + Navigator.popAndPushNamed(context, '/sitl'); + break; + } + }, + items: const [ + BottomNavigationBarItem( + icon: Icon(Icons.home_sharp), + label: 'Home', + ), + BottomNavigationBarItem( + icon: Icon(Icons.format_list_bulleted_sharp), + label: 'Logs', + ), + BottomNavigationBarItem( + icon: Icon(Icons.camera_sharp), + label: 'Camera', + ), + BottomNavigationBarItem( + icon: Icon(Icons.check_box_sharp), + label: 'SITL', + ) + ], + unselectedItemColor: Colors.black, + selectedItemColor: Colors.black, + showUnselectedLabels: true, + ); + } +} + +/// A placeholder screen with title text and the navigation bar +/// +/// This widget is a placeholder screen containing a title, +/// placeholder body, and the navigation bar. +class PlaceholderScreen extends StatelessWidget { + /// @brief Constructs a PlaceholderScreen widget. + const PlaceholderScreen({Key? key, required this.title}) : super(key: key); + + final String title; + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text(title), + ), + body: const Placeholder(), + bottomNavigationBar: const NavBar()); + } +} diff --git a/flutter_app/test/widget/nav_bar_widget_test.dart b/flutter_app/test/widget/nav_bar_widget_test.dart new file mode 100644 index 0000000..8eff77a --- /dev/null +++ b/flutter_app/test/widget/nav_bar_widget_test.dart @@ -0,0 +1,45 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:imacs/widgets/nav_bar_widget.dart'; + +void main() { + group('Navbar widget', () { + testWidgets('NavBar navigates to correct route', + (WidgetTester tester) async { + await tester.pumpWidget( + MaterialApp( + initialRoute: '/', + routes: { + '/': (context) => const Scaffold( + body: Text('Home Page'), bottomNavigationBar: NavBar()), + '/logs': (context) => const Scaffold( + body: Text('Logs Page'), bottomNavigationBar: NavBar()), + '/camera': (context) => const Scaffold( + body: Text('Camera Page'), bottomNavigationBar: NavBar()), + '/sitl': (context) => const Scaffold( + body: Text('SITL Page'), bottomNavigationBar: NavBar()), + }, + ), + ); + + expect(find.byType(BottomNavigationBar), findsOneWidget); + + // Test home icon tap + await tester.tap(find.text('Home')); + await tester.pumpAndSettle(); + expect(find.text('Home Page'), findsOneWidget); + + await tester.tap(find.text('Logs')); + await tester.pumpAndSettle(); + expect(find.text('Logs Page'), findsOneWidget); + + await tester.tap(find.text('Camera')); + await tester.pumpAndSettle(); + expect(find.text('Camera Page'), findsOneWidget); + + await tester.tap(find.text('SITL')); + await tester.pumpAndSettle(); + expect(find.text('SITL Page'), findsOneWidget); + }); + }); +}