5

I want to create a website using flutter web but I'm unable to navigate to sections in the same page. Here's an example of what I want to achieve using flutter.

P.S. Navigator is not working:

enter image description here

4 Answers4

8

I created an example with PageView

class MyHomePage extends StatelessWidget {

  var list = ["Home","Services", "Work", "About"];
  var colors = [Colors.orange, Colors.blue, Colors.red, Colors.green];

  PageController controller = PageController();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: SafeArea(
        child: Column(
          mainAxisSize: MainAxisSize.max,
          children: <Widget>[
            Row(
              children: <Widget>[
                Container(
                  width: 50,
                  height: 50,
                  margin: EdgeInsets.all(8),
                  decoration: BoxDecoration(
                    color: Colors.blue,
                    borderRadius: BorderRadius.circular(10)
                  ),
                ),
                Spacer(),
                Row(
                  children: List.generate(3, (index){
                    return GestureDetector(
                      onTap: (){
                        _scrollToIndex(index);
                      },
                      child: Container(
                        margin: EdgeInsets.all(8),
                        child: Text(
                          list[index+1]
                        ),
                      ),
                    );
                  }),
                )
              ],
            ),
            Expanded(
              child : PageView(
                scrollDirection: Axis.vertical,
                pageSnapping: false,
                controller: controller,
                children: List.generate(list.length, (index){
                  return Container(
                    width: MediaQuery.of(context).size.width,
                    height: double.maxFinite,
                    color: colors[index],
                    child: Center(
                      child: Text(
                          list[index],
                          style: TextStyle(
                            color: Colors.white,
                            fontSize: 50
                          ),
                      ),
                    ),
                  );
                })
              ),
            ),
          ],
        )
      ),
    );
  }

  void _scrollToIndex(int index) {
    controller.animateToPage(index + 1, duration: Duration(seconds: 2), curve: Curves.fastLinearToSlowEaseIn);
  }
}

The output:

enter image description here

Dharman
  • 30,962
  • 25
  • 85
  • 135
Josteve
  • 11,459
  • 1
  • 23
  • 35
0

ScrollController is the thing you are looking for.

Add a new one to your ScrolView and you can set where you want it to scroll to.

Dharman
  • 30,962
  • 25
  • 85
  • 135
Tim Hoeksema
  • 101
  • 4
0

Josteve mentioned a way of doing it. But I'd like to show the other way which provides more features as one would expect in the gif example you have put.

You can see the demo here: https://mohith7548.github.io/portfolio/

My project has 3 sections called About, Blog & Projects. It also has another top section called Home. So the order of screens is Home, About, Blog & Projects. Each section takes full-screen height & width. So the starting offset for these pages are [0 * screenHeight, 1 * screenHeight, 2 * screenHeight, 3 * screenHeight] respectively. screenHeight can be accessed by MediaQuery.of(context).size.height inside build method.

class Portfolio extends StatefulWidget {
  @override
  _PortfolioState createState() => _PortfolioState();
}

class _PortfolioState extends State<Portfolio> {
  ScrollController _scrollController;
  String _curNavItem;

  static double offsetHome = 0;
  static double offsetAbout = SizeConfig.screenHeight;
  static double offsetBlog = 2 * SizeConfig.screenHeight;
  static double offsetProjects = 3 * SizeConfig.screenHeight;

  @override
  void initState() {
    super.initState();
    _scrollController = ScrollController();
  }

  @override
  void dispose() {
    super.dispose();
    _scrollController.dispose();
  }

  void scrollTo(String title) {
    double offset = 0;
    switch (title) {
      case Constants.HOME:
        offset = offsetHome;
        break;
      case Constants.ABOUT:
        offset = offsetAbout;
        break;
      case Constants.BLOG:
        offset = offsetBlog;
        break;
      case Constants.PROJECTS:
        offset = offsetProjects;
        break;
    }

    setState(() {
      _curNavItem = title;
    });

    // animate to the pag
    _scrollController.animateTo(
      offset,
      duration: Duration(milliseconds: 500),
      curve: Curves.easeInOutQuart,
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: CustomScrollView(
        physics: PageScrollPhysics(), // use NeverScrollableScrollPhysics() to block user scrolling
        controller: _scrollController,
        slivers: <Widget>[
          // This is just SliverAppBar wrapped in InterheritedWidget called NavState
          // You can use just SliverAppBar
          NavState(
            curNavItem: _curNavItem,
            scrollTo: scrollTo,
            child: AppBanner(key: _appBannerKey), // SliverAppBar in another file
          ),
          SliverList(
            delegate: SliverChildListDelegate([
              About(),
              Blog(),
              Projects(),
            ]),
          )
        ],
      ),
    );
  }
}

Mohith7548
  • 1,282
  • 2
  • 16
  • 24
0

You can do this in different ways:

  1. TabBarView https://stackoverflow.com/a/60624536/10976088
  2. PageView https://stackoverflow.com/a/60778791/10976088
  3. NavigationRail https://api.flutter.dev/flutter/material/NavigationRail-class.html
  4. My method: Using a state management way to keep name or index of content pages and change visible page. I do it with the Riverpod package here:

Suppose you want to have a fixed SidebarView and HeaderView in all pages and also you have a ContentPage that will be changed. So you can have a RootPage including these 3 sections and change ContentPage by the riverpod, so that only ContentPage will be changed.

class RootPage extends StatelessWidget {
  const RootPage({Key? key}) : super(key: key);
  
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      drawer: SidebarView(),
      body: Row(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          if (Responsive.isDesktop(context))
            const Expanded(
              flex: 1,
              child: SidebarView(),
            ),
          Expanded(
            flex: 5,
            child: SafeArea(
              child: Column(
                mainAxisAlignment: MainAxisAlignment.start,
                children: [
                  HeaderView(),
                  Expanded(
                    child: Padding(
                      padding: const EdgeInsets.all(16),
                      child: Consumer(
                        builder: (context, ref, _) {
                          var watch = ref.watch(pageVisibleStateProvider);
                          return contentPageSelection(watch.state);
                        },
                      ),
                    ),
                  ),
                ],
              ),
            ),
          ),
        ],
      ),
    );
  }
}

simply change content page:

Widget contentPageSelection(String pageName){
  switch(pageName){
    case "page1":
      return Page1();
    case "page2":
      return Page2();
    case "page3":
      return Page3();
    default:
      return DefaultPage();
  }
}

where:

final pageVisibleStateProvider = StateProvider<String>((_) => "defaultPage");

and:

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

  @override
  Widget build(BuildContext context) {
    return Container(
      child: Text("sidebar content"),
    );
  }
}


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

  @override
  Widget build(BuildContext context) {
    return Container(
      child: Text("HeaderView content"),
    );
  }
}

Now you can change content page. for example you want to show Page2:

ElevatedButton(
   onPressed: (){
      ref.read(pageVisibleStateProvider.notifier).state = "page2";
   },
   child: Text("go to page 2"),
)

where page2 and other content pages only includes content not sidebar or header:

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

  @override
  Widget build(BuildContext context) {
    return Text("page2 content");
  }
}

Ghasem Sadeghi
  • 1,734
  • 13
  • 24