Search code examples
flutterdartbottom-navigation-bar

How to make a Navigator with the same destination as BottomNavigationBar?


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

Edit:.
I gave HomeScreen() a PageController and was able to take over the AppBar and BottomBar, but the BottomBar selection is not updated.

main.dart:

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

import 'src/app.dart';

void main() {
  WidgetsFlutterBinding.ensureInitialized();
  SystemChrome.setPreferredOrientations([
    DeviceOrientation.portraitUp,]);
  runApp(
    const MyApp()
  );
}

src/app.dart:

import 'package:flutter/material.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(
      //debugShowCheckedModeBanner: false,
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
        splashColor: Colors.transparent,
      ),
      home: BasePage(),
    );
  }
}

class BasePage extends StatefulWidget{
  @override
  _BasePage createState() => _BasePage();
}


class _BasePage extends State<BasePage> {
  final _pageController = PageController();
  int _currentIndex = 0;

  @override
  Widget build(BuildContext context) {

    var TabItem = {
      'home':[0, Icons.home, Colors.green, HomeScreen(pageController: _pageController)],
      'cam':[1, Icons.video_camera_back, Colors.lightGreen, const CamScreen()],
      'setting':[2, Icons.settings, Colors.yellow, const SettingScreen()]
    };

    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: PageView(
        controller: _pageController,
        physics: const NeverScrollableScrollPhysics(),
        children: TabItem.values.map((value) => value[3] as Widget).toList(),
      ),

      bottomNavigationBar: SizedBox(
        height: MediaQuery.of(context).size.height /8,
        child:BottomNavigationBar(
          currentIndex: _currentIndex,
          selectedFontSize: 0,
          unselectedFontSize: 0,
          items: TabItem.entries.map(
            (tabItem) => BottomNavigationBarItem(
              icon: _buildIcon(tabItem.value[1] as IconData, tabItem.value[2]as Color, selected: _currentIndex == tabItem.value[0]),
              label: tabItem.key,
            ),
          ).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,),
    );
  }
}

src/screens/home.dart:

import 'package:flutter/material.dart';

class HomeScreen extends StatelessWidget {
  final PageController pageController;
  HomeScreen({super.key, required this.pageController,});

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        Expanded(
          child: GestureDetector(
            onTap: (){pageController.jumpToPage(1);},
            child: Container(
              // Omit the code here.
            ),
          ),
        ),
      ],
    );
  }
}

src/screens/cam.dart

import 'package:flutter/material.dart';

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body:
          const Center(child: Text('camera', style: TextStyle(fontSize: 32.0))),
    );
  }
}

SOLVED src/app.dart

import 'package:flutter/material.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(
      //debugShowCheckedModeBanner: false,
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
        splashColor: Colors.transparent,
      ),
      home: BasePage(),
    );
  }
}

class BasePage extends StatefulWidget{
  @override
  _BasePage createState() => _BasePage();
}

class _BasePage extends State<BasePage> {
  final _pageController = PageController();
  int _currentIndex = 0;

  void changeIndex(index){
    setState(() {
      _currentIndex = index;
      _pageController.jumpToPage(index);
    });
  }
  
  @override
  Widget build(BuildContext context) {

    var TabItem = {
      'home':[0, Icons.home, Colors.green, HomeScreen(pageController: _pageController, changeIndex: changeIndex,)],
      'cam':[1, Icons.video_camera_back, Colors.lightGreen, const CamScreen()],
      'setting':[2, Icons.settings, Colors.yellow, const SettingScreen()]
    };

    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: PageView(
        controller: _pageController,
        physics: const NeverScrollableScrollPhysics(),
        children: TabItem.values.map((value) => value[3] as Widget).toList(),
      ),

      bottomNavigationBar: SizedBox(
        height: MediaQuery.of(context).size.height /8,
        child:BottomNavigationBar(
          currentIndex: _currentIndex,
          selectedFontSize: 0,
          unselectedFontSize: 0,
          items: TabItem.entries.map(
            (tabItem) => BottomNavigationBarItem(
              icon: _buildIcon(tabItem.value[1] as IconData, tabItem.value[2]as Color, selected: _currentIndex == tabItem.value[0]),
              label: tabItem.key,
            ),
          ).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,),
    );
  }
}

src/screens/home.dart

import 'package:flutter/material.dart';

class HomeScreen extends StatelessWidget {
  final PageController pageController;
  final void Function(int) changeIndex;
  const HomeScreen({super.key, required this.pageController, required this.changeIndex});


  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        Expanded(
          child: GestureDetector(
            onTap: (){changeIndex(1);},
            child: Container(
              // Omit the code here.
            ),
          ),
        ),
      ],
    );
  }
}

Solution

  • 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,),
        );
      }
    }
    

    Edit: The prblm when you click a btn on HomeScreen and go to other page, remove appbar and bottom nav bar is because you are using Navigator.push(). What you want to do is go from one page to another in pageview then use _pageController.jumpTo(). Just pass the controller to pages, which involve going to other pages. When you uses Navigator you are changing the whole screen but that's not what we want. We only want to change pages in page view to do that uses pageViewController