Flutter Basics for Beginners (Parte III)

Parabéns, pelo menos, a todos os que vivem na Sibéria com o início do verão!)))





A navegação é um assunto bastante complicado hoje.





Veremos como a navegação é organizada no Flutter, o que geralmente é necessário para alternar de uma tela para outra e, claro, não nos esqueceremos de passar argumentos entre as telas.





E, finalmente, um caso de uso muito comum: a criação de uma BottomNavigationBar.





'Bem, não vamos perder um minuto, vamos começar!





Nosso plano
  • Parte 1  - introdução ao desenvolvimento, primeiro apêndice, conceito de estado;





  • Parte 2 - arquivo pubspec.yaml e usando flutter na linha de comando;





  • Parte 3 (artigo atual) - BottomNavigationBar e Navigator;





  • Parte 4 - MVC. Usaremos esse padrão particular como um dos mais simples;





  • Parte 5 - pacote http. Criação da classe Repositório, primeiras solicitações, listagem de postagens;





  • Parte 6 - Trabalhar com imagens, exibir imagens em forma de grade, receber imagens da rede, adicionar as suas ao aplicativo;





  • Parte 7 - Criando seu próprio tema, adicionando fontes personalizadas e animações;





  • Parte 8 - Um pouco sobre testes;





Navegador e pilha de navegação

Flutter é muito simples de navegar, não há fragmentos ou atividades.





Muito simplesmente, cada página é um widget chamado Rota.





A navegação é feita por meio do objeto Navigator:





// Navigator.of(context)   Navigator
// : NavigatorState,   push  pop 
// push       Navigator
// pop       
// MaterialPageRoute     
//   
Navigator.of(context).push(
	MaterialPageRoute(builder: (context) => OurPage())
);
      
      



Vejamos a pilha do Navigator com um exemplo específico.





Temos duas telas: uma lista de livros e informações sobre um livro.





A primeira tela que aparece ao iniciar o aplicativo é a lista de livros:





:





.





Back Up ( ) :





push(route)



, pop()



.





!





My Little Pony .





pages:





:





import 'package:flutter/material.dart';

//  ,      ,   id
class Pony {
  final int id;
  final String name;
  final String desc;

  Pony(this.id, this.name, this.desc);
}

//   
// final   ,   
//      ponies
//   
final List<Pony> ponies = [
  Pony(
      0,
      "Twillight Sparkle",
      "Twilight Sparkle is the central main character of My Little Pony Friendship is Magic. She is a female unicorn pony who transforms into an Alicorn and becomes a princess in Magical Mystery Cure"
  ),
  Pony(
      1,
      "Starlight Glimmer",
      "Starlight Glimmer is a female unicorn pony and recurring character, initially an antagonist but later a protagonist, in the series. She first possibly appears in My Little Pony: Friends Forever Issue and first explicitly appears in the season five premiere."
  ),
  Pony(
      2,
      "Applejack",
      "Applejack is a female Earth pony and one of the main characters of My Little Pony Friendship is Magic. She lives and works at Sweet Apple Acres with her grandmother Granny Smith, her older brother Big McIntosh, her younger sister Apple Bloom, and her dog Winona. She represents the element of honesty."
  ),
  Pony(
      3,
      "Pinkie Pie",
      "Pinkie Pie, full name Pinkamena Diane Pie,[note 2] is a female Earth pony and one of the main characters of My Little Pony Friendship is Magic. She is an energetic and sociable baker at Sugarcube Corner, where she lives on the second floor with her toothless pet alligator Gummy, and she represents the element of laughter."
  ),
  Pony(
      4,
      "Fluttershy",
      "Fluttershy is a female Pegasus pony and one of the main characters of My Little Pony Friendship is Magic. She lives in a small cottage near the Everfree Forest and takes care of animals, the most prominent of her charges being Angel the bunny. She represents the element of kindness."
  ),
];

// PonyListPage    ,
// ..      
//   
class PonyListPage extends StatelessWidget {
  
  // build    , 
  //    
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text("Pony List Page")),
      //     
      body: Padding(
        //  EdgeInsets    double :
        // left, top, right, bottom -  , ,   
        // EdgeInsets.all(10) -      
        // EdgeInsets.only(left: 10, right: 15) -   
        //    
        // EdgeInsets.symmetric -   
        //    (left  right)    (top  bottom)
        padding: EdgeInsets.symmetric(vertical: 15, horizontal: 10),
        //   
          child: ListView(
            // map   , 
            //     
            //      ( Material).
            //  map   
            //   ,   
            //  Material 
            children: ponies.map<Widget>((pony) {
              // Material   ,
              //     
              //   ripple     
              return Material(
                color: Colors.pinkAccent,
                // InkWell  
                //  , : 
                child: InkWell(
                  // splashColor -  ripple 
                  splashColor: Colors.pink,
                  //    
                  onTap: () {
                    //   
                  },
                  //    
                  //  Container   Text
                  // Container    (padding)
                  //    (margin),
                  //   ,  ,
                  //     
                  child: Container(
                      padding: EdgeInsets.all(15),
                      child: Text(
                          pony.name,
                          style: Theme.of(context).textTheme.headline4.copyWith(color: Colors.white)
                      )
                  ),
                ),
              );
              // map  Iterable ,  
              //      toList() 
            }).toList(),
          )
      ),
    );
  }

}
      
      



, Dart , .





PonyDetailPage:






import 'package:flutter/material.dart';

import 'pony_list_page.dart';

// ,   PonyListPage  
//    
class PonyDetailPage extends StatelessWidget {
  //       id 
  final int ponyId;

  //  PonyDetailPage  ponyId,
  //     
  //  
  PonyDetailPage(this.ponyId);

  @override
  Widget build(BuildContext context) {
    //     id
    //  :   ponies 
    //   pony_list_page.dart
    final pony = ponies[ponyId];
    return Scaffold(
      appBar: AppBar(
        title: Text("Pony Detail Page"),
      ),
      body: Padding(
        //    
        padding: EdgeInsets.all(15),
        // Column      
        // crossAxisAlignment -    () 
        //   ()
        // mainAxisAlignment  
        //       
        //    
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.stretch,
          children: [
            Container(
                padding: EdgeInsets.all(10),
                //     color  Container,
                // ..  decoration  
                // color: Colors.pinkAccent,
                
                // BoxDecoration   ,
                //   Container,
                //  : gradient, borderRadius, border, shape
                //  boxShadow
                //        
                //  
                decoration: BoxDecoration(
                  borderRadius: BorderRadius.only(
                      topLeft: Radius.circular(15),
                      topRight: Radius.circular(15)
                  ),
         					//  Container'    BoxDecoration
                  color: Colors.pinkAccent,
                ),
                child: Text(
                    //   pony
                    pony.name,
                    style: Theme.of(context).textTheme.headline4.copyWith(color: Colors.white),
                )
            ),
            Container(
                padding: EdgeInsets.all(10),
                child: Text(
                    //   pony
                    pony.desc,
                    style: Theme.of(context).textTheme.bodyText1
                )
            )
          ],
        ),
      )
    );
  }
}
      
      



.





PonyListPage:





//    
onTap: () {
  //     :
  // Navigator.of(context).push(route)
  // PonyDetailPage  pony id,
  //    
	Navigator.push(context, MaterialPageRoute(
  	builder: (context) => PonyDetailPage(pony.id)
  ));
},
      
      



:





@override
  Widget build(BuildContext context) {
    //  MaterialApp -   , 
    //     
    // Material Design  .
    return MaterialApp(
      //  
      //  ,    
      title: 'Json Placeholder App',
      //  
      debugShowCheckedModeBanner: false,
      //  ,     
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      //      - PonyListPage
      home: PonyListPage(),
    );
  }
      
      



:





! , Back .





:





//  NavigatorState     
//    (PonyDetailPage)
//     ,    
Navigator.pop(context, result)
      
      



. .





: Navigator API 2.0, .





BottomNavigationBar.





BottomNavigationBar Navigator'

100% , , :





( , , ).





- .





models, tab.dart



:





Tab



TabItem



:





import 'package:flutter/material.dart';

//     
//   
class MyTab {
  final String name;
  final MaterialColor color;
  final IconData icon;

  const MyTab({this.name, this.color, this.icon});
}

//    
//   
//         :
// ,   
enum TabItem { POSTS, ALBUMS, TODOS }
      
      



, :





import 'package:flutter/material.dart';
import "../models/tab.dart";

//      
class HomePage extends StatefulWidget {
  @override
  _HomePageState createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {

  // GlobalKey    ,
  //      
  //  ,     
  // NavigatorState -  Navigator 
  final _navigatorKeys = {
    TabItem.POSTS: GlobalKey<NavigatorState>(),
    TabItem.ALBUMS: GlobalKey<NavigatorState>(),
    TabItem.TODOS: GlobalKey<NavigatorState>(),
  };

  //   
  var _currentTab = TabItem.POSTS;

  //   
  void _selectTab(TabItem tabItem) {
    setState(() => _currentTab = tabItem);
  }

  @override
  Widget build(BuildContext context) {
    // WillPopScope  
    //   Back
    return WillPopScope(
      //    back   
      //    :
      //        ()
      //    Back,     
      //       
      //  : c   ,    ,
      //       
      onWillPop: () async {
          if (_currentTab != TabItem.POSTS) {
            if (_currentTab == TabItem.TODOS) {
              _selectTab(TabItem.ALBUMS);
            } else {
              _selectTab(TabItem.POSTS);
            }
            return false;
          } else {
            return true;
          }

      },
      child: Scaffold(
        // Stack     
        //  ,    
        //  ,      
        body: Stack(children: <Widget>[
          _buildOffstageNavigator(TabItem.POSTS),
          _buildOffstageNavigator(TabItem.ALBUMS),
          _buildOffstageNavigator(TabItem.TODOS),
        ]),
        // MyBottomNavigation   
        bottomNavigationBar: MyBottomNavigation(
          currentTab: _currentTab,
          onSelectTab: _selectTab,
        ),
      ),);
  }

  //     - ,   
  Widget _buildOffstageNavigator(TabItem tabItem) {
    return Offstage(
      // Offstage   :
      //      
      //   ,    
      offstage: _currentTab != tabItem,
      // TabNavigator   
      child: TabNavigator(
        navigatorKey: _navigatorKeys[tabItem],
        tabItem: tabItem,
      ),
    );
  }

}
      
      



, Navigator , , .





Offstage



, .





back - WillPopScope



.





bottom_navigation.dart



:





import 'package:flutter/material.dart';
import '../models/tab.dart';

//    
// const ,  tabs  
//     
//     ,
//  ,     
const Map<TabItem, MyTab> tabs = {
  TabItem.POSTS : const MyTab(name: "Posts", color: Colors.red, icon: Icons.layers),
  TabItem.ALBUMS : const MyTab(name: "Albums", color: Colors.blue, icon: Icons.image),
  TabItem.TODOS : const MyTab(name: "Todos", color: Colors.green, icon: Icons.edit)
};

class MyBottomNavigation extends StatelessWidget {
  // MyBottomNavigation   onSelectTab
  //    
  MyBottomNavigation({this.currentTab, this.onSelectTab});

  final TabItem currentTab;
  // ValueChanged<TabItem> -  ,
  //   onSelectTab    ,
  //   TabItem 
  final ValueChanged<TabItem> onSelectTab;

  @override
  Widget build(BuildContext context) {
    //    BottomNavigationBar 
    //   
    return BottomNavigationBar(
        selectedItemColor: _colorTabMatching(currentTab),
        selectedFontSize: 13,
        unselectedItemColor: Colors.grey,
        type: BottomNavigationBarType.fixed,
        currentIndex: currentTab.index,
        //  
        items: [
          _buildItem(TabItem.POSTS),
          _buildItem(TabItem.ALBUMS),
          _buildItem(TabItem.TODOS),
        ],
        //     
        //      onSelectTab,
        //     
        onTap: (index) => onSelectTab(
            TabItem.values[index]
        )
    );
  }

  //   
  BottomNavigationBarItem _buildItem(TabItem item) {
    return BottomNavigationBarItem(
        //  
        icon: Icon(
          _iconTabMatching(item),
          color: _colorTabMatching(item),
        ),
        //    
        label: tabs[item].name,
    );
  }

  //   
  IconData _iconTabMatching(TabItem item) => tabs[item].icon;

  //   
  Color _colorTabMatching(TabItem item) {
    return currentTab == item ? tabs[item].color : Colors.grey;
  }

}


      
      



TabNavigator (tab_navigator.dart)



:





import 'package:flutter/material.dart';
import '../models/tab.dart';

import 'pony_list_page.dart';

class TabNavigator extends StatelessWidget {
  // TabNavigator :
  // navigatorKey -    NavigatorState
  // tabItem -   
  TabNavigator({this.navigatorKey, this.tabItem});

  final GlobalKey<NavigatorState> navigatorKey;
  final TabItem tabItem;

  @override
  Widget build(BuildContext context) {
    // -     
    //    navigatorKey 
    // ,   Navigator'
    // navigatorKey,      ,
    //       
    // Navigator'a,   !
    return Navigator(
      key: navigatorKey,
      // Navigator   initialRoute,
      //      
      //   .
      //      ,  ,
      //    initialRoute  /
      // initialRoute: "/",
      
      // Navigator      
      //     onGenerateRoute
      onGenerateRoute: (routeSettings) {
        //    
        Widget currentPage;
        if (tabItem == TabItem.POSTS) {
          //     PonyListPage
          currentPage = PonyListPage();
        } else if (tabItem == TabItem.POSTS) {
          currentPage = PonyListPage();
        } else {
          currentPage = PonyListPage();
        }
        //  Route (  )
        return MaterialPageRoute(builder: (context) => currentPage,);
      },
    );
  }

}
      
      



main.dart



:





return MaterialApp(
   //...
   //      
   home: HomePage(),
);
      
      



home_page.dart



:





Também é uma boa prática organizar o código corretamente, portanto, na pasta de páginas, crie uma nova pasta inicial e arraste nossos dois arquivos para lá:





E, finalmente, vamos fazer três páginas stub: PostListPage, AlbumListPage e TodoListPage:






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

//    
class PostListPage extends StatefulWidget {
  @override
  _PostListPageState createState() => _PostListPageState();
}

class _PostListPageState extends State<PostListPage> {
  
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Post List Page"),
      ),
      body: Container()
    );
  }
}
      
      



A mesma estrutura é para os outros dois.





Depois disso, iremos indicá-los em TabNavigator'e



:





onGenerateRoute: (routeSettings) {
  //    
  Widget currentPage;
  if (tabItem == TabItem.POSTS) {
  //   
    currentPage = PostListPage();
  } else if (tabItem == TabItem.ALBUMS) {
    currentPage = AlbumListPage();
  } else {
    currentPage = TodoListPage();
  }
   //  Route (  )
   return MaterialPageRoute(builder: (context) => currentPage);
},
      
      



Conclusão

Parabéns!





Estou sinceramente feliz e grato a você por suas boas críticas e apoio!





Links Úteis:





  • código fonte no Github





  • API Navigator 2.0





  • Navigation Cookbook





Vejo você em breve!








All Articles