Flutter Combat 2: добавьте верхнюю и нижнюю вкладки в приложение

внешний интерфейс Android iOS Flutter Dart

Сразу после предыдущегоЕсть боковое приложение, на этот раз мы добавим верхнюю и нижнюю вкладки в приложение, чтобы оно было ближе к процедуре размещения информации в большинстве основных приложений.Студенты, которым не терпится прочитать исходный код, могут щелкнуть, чтобы войти в мойgitКод загрузки репозитория.

Ключевые элементы табуляции

  • TabControllerЭтоTabконтроллер страницы, используемый для определенияTabТакже можно настроить координаты метки и страницы содержимого, а также эффект анимации переключения страницы метки.

TabController обычно используется в элементах управления с отслеживанием состояния для адаптации к сценариям, в которых количество и содержимое вкладок изменяются динамически.Если вкладки статичны и фиксированы в приложении, к элементам управления без сохранения состояния можно добавить упрощенную версию.DefaultTabControllerВ конце концов, повысить работоспособность, в конце концов, элементы управления беззаботом являются более ресурсоиданными и работают быстрее, чем устойчивые элементы управления.

  • TabBar TabСтраницаTitleуправление, переключениеTabЗапись страницы, обычно помещаемая вAppBarИспользуется под управлением, внутри есть свойство **Title*. Его дочерние элементы располагаются по горизонтали и по горизонтали.Если вам нужно расположить по вертикали, пожалуйста, используйтеColumnилиListViewКонтрольная упаковка. дочерний элементTabмассив типов.

  • TabBarView TabКонтейнер содержимого страницы, внутри которогоTabОсновное содержание страницы. Дочерние элементы могут быть несколькими элементами управления различных типов.

Как использовать вкладку

Элементы управления с отслеживанием состоянияTabController

TabПереход страницы сопровождается анимацией, поэтомуStateдобавить один в классSingleTickerProviderStateMixin:

  //定义有状态控件
  class HomePage extends StatefulWidget {
    @override
    _HomePageState createState() => new _HomePageState();
  }

  //用于使用到了一点点的动画效果,因此加入了SingleTickerProviderStateMixin
  class _HomePageState extends State<HomePage> with SingleTickerProviderStateMixin{
    ...
  }

Затем к подклассу управления с отслеживанием состоянияStateИнициализировать контроллер вTabController:

  @override
  void initState() {
    super.initState();
    _tabController = new TabController(
      vsync: this,     //动画效果的异步处理,默认格式,背下来即可
      length: 3      //需要控制的Tab页数量
    );    
  }
  //当整个页面dispose时,记得把控制器也dispose掉,释放内存
  @override
  void dispose() {
    _tabController .dispose();
    super.dispose();
  }

затем кTabBarа такжеTabBarViewсерединаcontrollerвызов контроллера из свойства_tabController

  //标签页标题
  new TabBar(
          tabs: [    //注意TabBar的子元素为Tab类型的数组
            new Tab(icon: new Icon(Icons.directions_car)),
            new Tab(icon: new Icon(Icons.directions_transit)),
            new Tab(icon: new Icon(Icons.directions_bike)),
          ]
  //标签页内容区域
  new TabBarView(
        children: [
          new Icon(Icons.directions_car),
          new Icon(Icons.directions_transit),
          new Icon(Icons.directions_bike),
        ]

Наконец, мы определяемTabBarа такжеTabBarViewПоместите его туда, где он вам нужен, например:

new Scaffold(
      appBar: new AppBar(
        backgroundColor: Colors.deepOrange,
        title: new Text('title'),
      ),
      ....
      body: new TabBarView(        //TabBarView呈现内容,因此放到Scaffold的body中
          controller: _bottomNavigation,      //配置控制器
          children:  [      //注意顺序与TabBar保持一直
            new News(data: '参数值'),    //上一篇定义好的子页面
            new TabPage2(),
            new TabPage3(),
          ]
        ),
      bottomNavigationBar: new Material(    //为了适配主题风格,包一层Material实现风格套用
        color: Colors.deepOrange,   //底部导航栏主题颜色
        child: new TabBar(        //TabBar导航标签,底部导航放到Scaffold的bottomNavigationBar中
          controller: _bottomNavigation,      //配置控制器
          tabs: _bottomTabs,
          indicatorColor: Colors.white, //tab标签的下划线颜色
        ),
      ) 
    );

безгражданские элементы управленияDefaultTabController

DefaultTabControllerГораздо проще, поскольку он используется в элементах управления без сохранения состояния, используйтеDefaultTabControllerпакет необходимо использоватьTabстраница в:

class TabPage3 extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return  new DefaultTabController(
        length: 3,
        child: new Scaffold(
          appBar: new AppBar(
            backgroundColor: Colors.orangeAccent,
            title: new TabBar(
              tabs: [
                new Tab(icon: new Icon(Icons.directions_car)),
                new Tab(icon: new Icon(Icons.directions_transit)),
                new Tab(icon: new Icon(Icons.directions_bike)),
              ],
              indicatorColor: Colors.white,
            ),
          ),
          body: new TabBarView(
            children: [
              new Icon(Icons.directions_car),
              new Icon(Icons.directions_transit),
              new Icon(Icons.directions_bike),
            ],
          ),
        ),
      );
  }
}

DefaultTabControllerа такжеTabControllerРазница в использовании в основном заключается в определении контроллера,TabBarа такжеTabBarViewиспользуются точно так же, обычно вверх и внизTabВкладки страниц размещаются вScaffoldконтрольappBarа такжеbottomNavigationBarproperties, а затем заполняем APP следующим стилем:

效果图

структура кода

Как показано на рисунке выше, приложение начинается с нижнегоTabПанель навигации — это главный вход, и каждыйTabу каждого есть своя высшая средняяTabстраницу, поэтому давайте организуем структуру кода:

代码框架

  • _HomePageStateглавная страница приложенияHomePageподклассState

  • пройти черезScaffoldНижнийbottomNavigationBarразмещение недвижимостиTabBar, строитьTabпанель вкладок страницы

  • положить вScaffoldизbodyатрибут поставитьTabBarView,TabBarViewизchildrenтри внешнихdartстраница управления, определяемая файлом

  • внешнийdartСтраницы управления, определенные в файле, имеют собственные стили.TabСтраница закладок

  • TabОбщие свойства страниц могут быть определены заранее для массивовListв, вTabBarа такжеTabBarViewИзвлечь путем обходаListСоздание дочерних элементов из значения может повысить эффективность обслуживания кода:

    //существуетStatefulWidgetВ элементе управления динамическую загрузку страницы вкладок можно реализовать, изменив следующие массивы окончательный список myTabs = [ новая вкладка (текст: «Tab1»), новая вкладка (текст: «Tab2»), новая вкладка (текст: «Tab3»), новая вкладка (текст: «Tab4»), новая вкладка (текст: «Tab5»), новая вкладка (текст: «Tab6»), новая вкладка (текст: «Tab7»), новая вкладка (текст: «Tab8»), новая вкладка (текст: «Tab9»), новая вкладка (текст: «Tab10»), новая вкладка (текст: «Tab11»), ];

    Widget build(BuildContext context) {
      return new Scaffold(
        appBar: new AppBar(
          backgroundColor: Colors.orangeAccent,
          title: new TabBar(
            controller: _tabController,
            tabs: myTabs,    //使用Tab类型的数组呈现Tab标签
            indicatorColor: Colors.white,
            isScrollable: true,   
          ),
        ),
        body: new TabBarView(
          controller: _tabController,
          children: myTabs.map((Tab tab) {    //遍历List<Tab>类型的对象myTabs并提取其属性值作为子控件的内容
            return new Center(child: new Text(tab.text+'   '+widget.data)); //使用参数值
          }).toList(),
        ),
      );
    }
    

Используйте полученные параметры

из-заStatelessWidgetа такжеStatefulWidgetСтруктура страницы отличается, и способ использования параметров, полученных извне, также немного отличается, что кратко изложено здесь.

  • StatelessWidgetСпособ получения и использования параметров определениеStatelessWidgetконтроль, добавитьfinalпеременные типа, такие какpageText, который используется для резервирования места для значения параметра, и добавьте значение параметра в конструктор:

    класс SidebarPage расширяет StatelessWidget { final String pageText; //Определяем константу для сохранения параметров, полученных при переходе SidebarPage(this.pageText);//Конструктор, получаем параметры ... }

При использовании параметров вы можете напрямую ссылаться на них:

Widget build(BuildContext context) {
    return new Scaffold(
      appBar: new AppBar(title: new Text(pageText),),   //将参数当作页面标题
      body: new Center(
        child: new Text('pageText'),
      ),
    );
  }

При передаче параметров извне вы можете напрямую заполнить значения параметров в конструкторе:

Navigator.of(context).push(new MaterialPageRoute(builder: 
    (BuildContext context) => new SidebarPage('First Page')));    //在new方法时调用控件的构造函数传入参数值
  • StatefulWidgetСпособ получения и использования параметров в сравнении сStatelessWidgetнемного сложно. При определении конструктора требуется объявление по умолчанию.key:

    класс TabPage1 расширяет StatefulWidget { const TabPage1({ Key key , this.data}) : super(key: key);//Добавляем параметры в конструктор final String data; // выделяем место для параметров @переопределить _MyTabbedPageState createState() => новый _MyTabbedPageState(); }

При использовании из-заStateПодкласс для достижения определенного содержимого страницы,StateПодкласс использует суперклассTabPage1При настройке параметра необходимо добавить ключевое слово ***widget*** перед названием параметра:

class _MyTabbedPageState extends State<TabPage1> {
  ...
  new Center(child: new Text(tab.text+'   '+widget.data));   //使用参数值,需在参数名前增加widget前缀
  ...
}

При передаче параметров извне вам необходимо объявить имя параметра:

new TabBarView
    controller: _bottomNavigation,
    children:  [      
      new TabPage1(data: '参数值'),    //new方法调用构造函数时,还需要声明参数名称
      new TabPage2(),
      new TabPage3(),
    ]
  )

Хаоле, на сегодня все, все идите и скачивайте мойgitПопробуем эффект из исходников.В коде есть дополнительные комментарии, а также есть отдельные описания характеристик некоторых свойств управления.Я считаю, что прочитав исходники, вы также сможете реализовать эффект самостоятельно.

Кстати, поделитесь минным полем, потому что когда я создавал этот проект, я использовал команду **flutter create [APPname1]Я создал этот проект по типу APP, но обнаружил, что APPname1 (имя прокси, а не настоящее имя) звучит плохо, поэтому я хотел переименовать проект в APPname2, поэтому я сослался на то, что написал ранее.Как упаковать Android?,Переименуйте папку проекта в APPname2 и очень умышленно поместите файл _android\app\src\main\AndroidManifest.xml_ в каталог проекта, поместитеpackageа такжеandroid:labelВсе поменялось на APPname2, так что неожиданно стало грустно, командаflutter fun**Отчет об ошибках,Не удается запустить приложение, после восстановления конфигурации,Не удается запустить приложение, даже если вы пытаетесь найти APPname1 по всему тексту, замените его на APPname2 в соответствии с указанным форматом или измените его обратно в обратном порядке.Не могу запустить приложение, это было 1 час ночи. . . Надлежащая история крови и слез, поэтому я торжественно предупреждаю всех:

Не меняйте легко имя проекта в различных файлах конфигурации проекта! Не меняйте легко имя проекта в различных файлах конфигурации проекта! Не меняйте легко имя проекта в различных файлах конфигурации проекта!В противном случае вы станете следующим рыбным шариком, бьющим вас в грудь перед компьютером. Какой? Как мне выздороветь? Конечно же благодаря git.

Спасибо за вашу поддержку, пожалуйста, следуйте за мнойФлаттер круг, Делайте больше, вы также можете присоединиться к **китайскому сообществу флаттера (официальная группа QQ: 338252156)**, чтобы расти вместе, спасибо~