skip to Main Content

I want to add a button to ‘src/screens/home.dart’ that has the same transition as the BottomNavigationBar in ‘src/app.dart’. Of course, keeping the AppBar and BottomNavigationBar.

In my code, if I move from the button in home to cam Screen, it leaves app.dart.
It is also plausible that the UI should be reviewed.

After that transition, I want the BottomNavigationBar icon to light up. When moving to a screen with no icons, I want all icons to be dimmed.
Same as the ‘app.dart _buildIcon’ process.

I started learning Flutter yesterday, so if there’s any weird code, please let me know.
Thank you.

enter image description here

src/app.dart

import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';

import 'screens/home.dart';
import 'screens/cam.dart';
import 'screens/setting.dart';

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
        splashColor: Colors.transparent,
      ),
      home: const BasePage(),
    );
  }
}

enum TabItem {
  home(
    title: 'home',
    icon: Icons.home,
    color: Colors.green,
    page: HomeScreen()
  ),
  cam(
    title: 'cam',
    icon: Icons.video_camera_back,
    color: Colors.lightGreen,
    page: CamScreen()
  ),
  setting(
    title: 'setting',
    icon: Icons.settings,
    color: Colors.cyan,
    page: SettingScreen()
  );

  const TabItem({
    required this.title,
    required this.icon,
    required this.color,
    required this.page,
  });
  final String title;
  final IconData icon;
  final Color color;
  final Widget page;
}


final _navigatorKeys = <TabItem, GlobalKey<NavigatorState>>{
  TabItem.home: GlobalKey<NavigatorState>(),
  TabItem.cam: GlobalKey<NavigatorState>(),
  TabItem.setting:GlobalKey<NavigatorState>(),
};


class BasePage extends HookWidget {
  const BasePage({super.key});

  @override
  Widget build(BuildContext context) {
    final currentTab = useState(TabItem.home);

    return Scaffold(
      appBar: AppBar(
        toolbarHeight: MediaQuery.of(context).size.height / 16,
        title: Image.asset('assets/images/logo.png', width: 200, fit: BoxFit.contain,),
        centerTitle: true,
      ),

      body: Stack(
        children: TabItem.values.map(
          (tabItem) => Offstage(
            offstage: currentTab.value != tabItem,
            child: Navigator(
              key: _navigatorKeys[tabItem],
              onGenerateRoute: (settings) {
                return MaterialPageRoute<Widget>(
                  builder: (context) => tabItem.page,
                );
              },
            ),
          ),
        ).toList(),
      ),

      bottomNavigationBar: SizedBox(
        height: MediaQuery.of(context).size.height /8,
        child:BottomNavigationBar(
          currentIndex: TabItem.values.indexOf(currentTab.value),
          selectedFontSize: 0,
          unselectedFontSize: 0,
          onTap: (index) {
            final selectedTab = TabItem.values[index];
            if (currentTab.value == selectedTab) {
              _navigatorKeys[selectedTab]
                  ?.currentState
                  ?.popUntil((route) => route.isFirst);
            } else {
              currentTab.value = selectedTab;
            }
          },

          items: TabItem.values.map(
            (tabItem) => BottomNavigationBarItem(
              icon: _buildIcon(tabItem.icon, tabItem.color, selected: currentTab.value==tabItem),
              label: tabItem.title,
            ),
          ).toList(),
          type: BottomNavigationBarType.fixed,
        ),
      ),
    );
  }

  Widget _buildIcon(IconData icon, Color color, {bool selected=false}) {
    Color filteredColor = selected ? color : color.withOpacity(0.3);
    return Container(
      width: 60, height: 60,
      decoration: BoxDecoration(shape: BoxShape.circle, color: filteredColor),
      child: Icon(icon, size: 50, color: Colors.white,),
    );
  }
}

src/screens/home.dart

import 'package:flutter/material.dart';

import 'cam.dart';
import 'setting.dart';

class HomeScreen extends StatelessWidget {
  const HomeScreen({super.key});

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        Expanded(
          child: GestureDetector(
            onTap: (){Navigator.push(context, MaterialPageRoute(builder: (context) => const CamScreen(),));},
            child: Container(
              // Omit the code here.
            ),
          ),
        ),
      ],
    );
  }
}

EDIT

src/app.dart

import 'package:flutter/material.dart';

import 'screens/home.dart';
import 'screens/cam.dart';
import 'screens/setting.dart';


class MyApp extends StatefulWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
        splashColor: Colors.transparent,
      ),
    );
  }
  @override
  State createState() => BasePage();
}


enum TabItem {
  home(
    title: 'home',
    icon: Icons.home,
    color: Colors.green,
    page: HomeScreen()
  ),
  cam(
    title: 'cam',
    icon: Icons.video_camera_back,
    color: Colors.lightGreen,
    page: CamScreen()
  ),
  setting(
    title: 'setting',
    icon: Icons.settings,
    color: Colors.cyan,
    page: SettingScreen()
  );

  const TabItem({
    required this.title,
    required this.icon,
    required this.color,
    required this.page,
  });
  final String title;
  final IconData icon;
  final Color color;
  final Widget page;
}

final _navigatorKeys = <TabItem, GlobalKey<NavigatorState>>{
  TabItem.home: GlobalKey<NavigatorState>(),
  TabItem.cam: GlobalKey<NavigatorState>(),
  TabItem.setting:GlobalKey<NavigatorState>(),
};


class BasePage extends State<MyApp> {
  final _pageController = PageController();
  int _currentIndex = 0;
  
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        toolbarHeight: MediaQuery.of(context).size.height / 16,
        title: Image.asset('assets/images/logo.png', width: 200, fit: BoxFit.contain,),
        centerTitle: true,
      ),

      body: Expanded(
        child: PageView(
          controller: _pageController,
          physics: const NeverScrollableScrollPhysics(),
          onPageChanged: (index){
            setState(() {
              _currentIndex = index;
            });
          },
          children: TabItem.values.map(
            (tabItem) => tabItem.page
          ).toList(),
        ),
      ),

      bottomNavigationBar: SizedBox(
        height: MediaQuery.of(context).size.height /8,
        child:BottomNavigationBar(
          currentIndex: _currentIndex,
          selectedFontSize: 0,
          unselectedFontSize: 0,
          items: TabItem.values.map(
            (tabItem) => BottomNavigationBarItem(
              icon: _buildIcon(tabItem.icon, tabItem.color, selected: _currentIndex==tabItem.index),
              label: tabItem.title,
            ),
          ).toList(),
          onTap: (index) {
            setState(() {
              _currentIndex = index;
            });
            _pageController.jumpToPage(index);
            //_pageController.animateToPage(index, duration: const Duration(0), curve: Curves.easeIn);
          },
          type: BottomNavigationBarType.fixed,          
        ),
      ),
    );
  }

  Widget _buildIcon(IconData icon, Color color, {bool selected=false}) {
    Color filteredColor = selected ? color : color.withOpacity(0.3);
    return Container(
      width: 60, height: 60,
      decoration: BoxDecoration(shape: BoxShape.circle, color: filteredColor),
      child: Icon(icon, size: 50, color: Colors.white,),
    );
  }
}

2

Answers


  1. Maybe you can give a try to PageView widget.

    Just wrap the PageView in a Expanded widget & wrap it with your appbar & bottom tabbar.
    To change page, use the page view controller, the animation will be animated (can be disabled of course).

    https://api.flutter.dev/flutter/widgets/PageView-class.html

    Login or Signup to reply.
  2. Check out the below code. In your edited code you were handling MyApp widget incorrectly, you need to pass a home widget to MaterialApp.
    And you don’ need onPageChange here beacuse it is used when you scroll in PageView and want to do some changes on page change but in your case you are manually changing it on bottom navigation bar click

    void main()  {
      WidgetsFlutterBinding.ensureInitialized();
      runApp(const MyApp());
    }
    
    class MyApp extends StatefulWidget {
      const MyApp({Key? key}) : super(key: key);
    
      @override
      State<MyApp> createState() => _MyAppState();
    }
    
    class _MyAppState extends State<MyApp> {
      @override
      void initState() {
        super.initState();
        // postData();
        // fetchIPAddress();
      }
    
      @override
      Widget build(BuildContext context) {
        return const MaterialApp(
          debugShowCheckedModeBanner: false,
          home: BasePage(),
        );
      }
    }
    
    enum TabItem {
      home(
          title: 'home',
          icon: Icons.home,
          color: Colors.green,
          page: HomeScreen()
      ),
      cam(
          title: 'cam',
          icon: Icons.video_camera_back,
          color: Colors.lightGreen,
          page: CamScreen()
      ),
      setting(
          title: 'setting',
          icon: Icons.settings,
          color: Colors.cyan,
          page: SettingScreen()
      );
    
      const TabItem({
        required this.title,
        required this.icon,
        required this.color,
        required this.page,
      });
      final String title;
      final IconData icon;
      final Color color;
      final Widget page;
    }
    
    class BasePage extends StatefulWidget {
      const BasePage({super.key});
    
      @override
      State<BasePage> createState() => _BasePageState();
    }
    
    class _BasePageState extends State<BasePage> {
      final _pageController = PageController();
      int _currentIndex = 0;
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            toolbarHeight: MediaQuery.of(context).size.height / 16,
            title: Image.asset('assets/images/logo.png', width: 200, fit: BoxFit.contain,),
            centerTitle: true,
          ),
    
          body: Expanded(
            child: PageView(
              controller: _pageController,
              physics: const NeverScrollableScrollPhysics(),
              children: TabItem.values.map(
                      (tabItem) => tabItem.page
              ).toList(),
            ),
          ),
    
          bottomNavigationBar: SizedBox(
            height: MediaQuery.of(context).size.height /8,
            child:BottomNavigationBar(
              currentIndex: _currentIndex,
              selectedFontSize: 0,
              unselectedFontSize: 0,
              items: TabItem.values.map(
                    (tabItem) => BottomNavigationBarItem(
                  icon: _buildIcon(tabItem.icon, tabItem.color, selected: _currentIndex==tabItem.index),
                  label: tabItem.title,
                ),
              ).toList(),
              onTap: (index) {
                setState(() {
                  _currentIndex = index;
                  _pageController.jumpToPage(index);
                });
              },
              type: BottomNavigationBarType.fixed,
            ),
          ),
        );
      }
    
      Widget _buildIcon(IconData icon, Color color, {bool selected=false}) {
        Color filteredColor = selected ? color : color.withOpacity(0.3);
        return Container(
          width: 60, height: 60,
          decoration: BoxDecoration(shape: BoxShape.circle, color: filteredColor),
          child: Icon(icon, size: 50, color: Colors.white,),
        );
      }
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search