Flutter主从界面布局插件master_detail_scaffold的使用

发布于 1周前 作者 zlyuanteng 来自 Flutter

Flutter主从界面布局插件master_detail_scaffold的使用

插件简介

master_detail_scaffold 是一个Flutter包,它包含了一些帮助实现响应式主-从布局的部件。该部件基于Material Design Scaffold构建,并且受到了这篇文章探索响应式布局的启发。因此,它可能并不适用于所有场景(例如嵌套/多个主-从布局),因为它是在社区成员可能会发现它有用的前提下共享的。你可以在这里查看Web上的示例运行效果。

注意: 该插件使用的是命令式的Navigator (1.0) API。目前没有计划将其迁移到Navigator 2.0,因此需要Navigator 2.0的用户应该考虑分叉这个项目。

开始使用

该包暴露了一个MasterDetailScaffold部件,它是基于Flutter的Scaffold部件构建的。因此,你可以期望大多数你熟悉的属性都存在,并且可以像平常使用Scaffold部件一样使用它。创建一个类并在你的build方法中使用MasterDetailScaffold部件。

示例代码

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

void main() => runApp(MyApp());

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  DummyItem? _selectedItem;

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Master-detail Flow Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MasterDetailScaffold(
        onDetailsPaneRouteChanged:
            (String? route, Map<String, String>? parameters) {
          setState(() {
            if (route == RouteNames.itemDetails) {
              _selectedItem = content.items.firstWhere(
                  (item) => item.id == parameters!['id'],
                  orElse: null);
              return;
            }
            _selectedItem = null;
          });
        },
        twoPanesWidthBreakpoint: 600,
        initialRoute: RouteNames.home,
        detailsRoute: RouteNames.itemDetails,
        initialAppBar: AppBar(
          title: Text('Master-detail Flow Demo'),
        ),
        masterPaneWidth: 400,
        masterPaneBuilder: (BuildContext context) => ItemsList(
          selectedItem: _selectedItem,
        ),
        detailsPaneBuilder: (BuildContext context) =>
            _selectedItem == null
                ? SizedBox.shrink()
                : ItemDetails(item: _selectedItem!),
        detailsAppBar: AppBar(
          // 使用 [Builder] 确保标题根据选定项重建
          title: Builder(
            builder: (context) => _selectedItem == null
                ? SizedBox(
                    height: 0,
                    width: 0,
                  )
                : Text(_selectedItem!.title),
          ),
        ),
        floatingActionButton: Visibility(
          visible: _selectedItem != null,
          child: Builder(
            builder: (context) => FloatingActionButton(
              child: Icon(Icons.reply),
              onPressed: () {
                ScaffoldMessenger.of(context).showSnackBar(
                  SnackBar(
                    content: Text('Replying to ${_selectedItem!.title}'),
                  ),
                );
              },
            ),
          ),
        ),
      ),
    );
  }
}

关键配置项说明

  • twoPanesWidthBreakpoint: 显示主和从面板一起的宽度断点。当两个面板可见时隐藏返回按钮。
  • initialRoute: 当详情面板未显示任何内容时使用的路由名称。
  • detailsRoute: 当详情面板显示内容时使用的路由名称。
  • masterPaneWidth: 主面板的宽度。当主和从面板同时显示时适用。
  • masterPaneBuilder: 决定在主面板中显示什么内容。通常是一个项目列表,具体实现留给开发者。
  • detailsPaneBuilder: 决定在从面板中显示什么内容。当主和从面板同时显示时,默认占据屏幕剩余宽度。
  • onDetailsPaneRouteChanged: 详情面板中的路由更改时触发的回调。用于获取路由/路径和查询字符串参数,以便显示适当的内容。

导航

要触发详情面板中的导航,可以通过调用MasterDetailScaffold.of(context).detailsPaneNavigator获取与详情面板关联的navigator。通常情况下不需要这样做,直接调用Navigator.of(context)即可,但它似乎无法获取到与详情面板关联的navigator。URI-based导航是通过命名路由实现的。默认情况下,页面转换应用的是与MaterialPageRoute相同的动画,但当两个面板同时显示时,详情更改时不使用动画。如果你想使用不同的过渡样式,可以使用pageRouteBuilder属性指定。

请注意,我发现输入一个应该跳转到特定详情页的URL不起作用。这可能是由于Flutter的Web开发支持尚未稳定。如果你找到了解决方案,请提交一个pull request。

完整示例可以在GitHub仓库中找到。


更多关于Flutter主从界面布局插件master_detail_scaffold的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter主从界面布局插件master_detail_scaffold的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,以下是一个使用 master_detail_scaffold 插件在 Flutter 中实现主从界面布局的示例代码。master_detail_scaffold 插件可以方便地创建具有主视图和从视图(如侧边导航和详情内容)的布局。

首先,确保在 pubspec.yaml 文件中添加 master_detail_scaffold 依赖:

dependencies:
  flutter:
    sdk: flutter
  master_detail_scaffold: ^x.y.z  # 替换为最新版本号

然后,运行 flutter pub get 来获取依赖。

接下来是主程序代码示例:

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

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Master Detail Scaffold Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MasterDetailScaffold(
        drawer: Drawer(
          child: ListView(
            padding: EdgeInsets.zero,
            children: <Widget>[
              DrawerHeader(
                child: Text('Drawer Header'),
                decoration: BoxDecoration(
                  color: Colors.blue,
                ),
              ),
              ListTile(
                leading: Icon(Icons.home),
                title: Text('Home'),
                onTap: () {
                  Navigator.pop(context); // 关闭抽屉
                  // 可以在这里添加其他逻辑,比如更新主视图内容
                },
              ),
              ListTile(
                leading: Icon(Icons.settings),
                title: Text('Settings'),
                onTap: () {
                  Navigator.pop(context); // 关闭抽屉
                  // 可以在这里添加其他逻辑,比如打开设置页面
                },
              ),
            ],
          ),
        ),
        master: Master(
          title: 'Master List',
          child: ListView.builder(
            itemCount: 20,
            itemBuilder: (context, index) {
              return ListTile(
                title: Text('Item $index'),
                onTap: () {
                  ScaffoldMessenger.of(context).showSnackBar(
                    SnackBar(content: Text("Selected Item $index")),
                  );
                  // 更新从视图内容
                  _updateDetail(context, 'Detail for Item $index');
                },
              );
            },
          ),
        ),
        detail: Detail(
          title: 'Detail View',
          child: Center(
            child: Text('Initial Detail Content'),
          ),
        ),
        floatingActionButton: FloatingActionButton(
          onPressed: () {
            // 触发一些操作,比如打开一个新的页面或者刷新数据
          },
          tooltip: 'Action',
          child: Icon(Icons.add),
        ),
        onItemSelected: (index) {
          // 可以在这里处理选中项的逻辑,比如更新从视图内容
          // 这里我们假设 index 是从 master 列表传过来的,可以根据需要更新 detail 内容
          _updateDetail(context, 'Detail for Selected Item $index');
        },
      ),
    );
  }

  void _updateDetail(BuildContext context, String newDetailContent) {
    // 使用 MasterDetailScaffoldState 更新详情视图
    final MasterDetailScaffoldState? scaffoldState =
        MasterDetailScaffold.of(context);
    if (scaffoldState != null) {
      scaffoldState.updateDetail(
        Detail(
          title: 'Updated Detail View',
          child: Center(
            child: Text(newDetailContent),
          ),
        ),
      );
    }
  }
}

class Master extends StatelessWidget {
  final String title;
  final Widget child;

  const Master({Key? key, required this.title, required this.child})
      : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Container(
      color: Colors.grey[200],
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: <Widget>[
          AppBar(
            title: Text(title),
            automaticallyImplyLeading: false,
          ),
          Expanded(child: child),
        ],
      ),
    );
  }
}

class Detail extends StatelessWidget {
  final String title;
  final Widget child;

  const Detail({Key? key, required this.title, required this.child})
      : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Container(
      color: Colors.white,
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: <Widget>[
          AppBar(
            title: Text(title),
            leading: IconButton(
              icon: Icon(Icons.arrow_back),
              onPressed: () {
                ScaffoldMessenger.of(context).pop(); // 返回主视图
              },
            ),
          ),
          Expanded(child: child),
        ],
      ),
    );
  }
}

在这个示例中,我们创建了一个包含抽屉导航(Drawer)、主视图(Master)和从视图(Detail)的应用程序。MasterDetailScaffold 插件帮助我们管理这些视图的布局和交互。主视图是一个简单的列表,点击列表项会更新从视图的内容,并通过 MasterDetailScaffoldState 更新详情视图。

注意:由于 master_detail_scaffold 插件的具体 API 可能会随着版本更新而变化,请参考插件的官方文档和示例代码以获取最新和最准确的用法。

回到顶部