Flutter滑动顶部面板插件sliding_top_panel的使用

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

Flutter滑动顶部面板插件sliding_top_panel的使用

插件介绍

sliding_top_panel 是一个 Flutter 插件,允许你在应用中显示一个可滑动的顶部面板。这个插件在 Android 和 iOS 平台上都能正常工作。

安装

在你的 pubspec.yaml 文件中添加以下依赖:

dependencies:
  sliding_top_panel: ^0.0.7

示例

以下是使用 sliding_top_panel 的一个完整示例。这个示例展示了如何创建一个包含可滑动顶部面板的页面,并且包含了一个浮动按钮来控制面板的打开和关闭。

完整示例代码

import 'dart:math' as math;
import 'package:flutter/material.dart';
import 'package:sliding_top_panel/sliding_top_panel.dart';

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

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      debugShowCheckedModeBanner: false,
      home: const MyHomePage(title: 'Example Sliding Panel'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({Key? key, required this.title}) : super(key: key);

  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  final ValueNotifier<bool> _isPanelVisible = ValueNotifier(false);
  final SlidingPanelTopController _controller = SlidingPanelTopController();

  @override
  void initState() {
    super.initState();
    _controller.addListener(listenerController);
  }

  void listenerController() {
    _isPanelVisible.value = _controller.isPanelOpen;
  }

  @override
  void dispose() {
    _controller.removeListener(listenerController);
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: SlidingTopPanel(
        // maxHeight: 100,
        decorationPanel: const BoxDecoration(
          color: Colors.white,
          borderRadius: BorderRadius.only(
            bottomLeft: Radius.circular(10),
            bottomRight: Radius.circular(10),
          ),
        ),
        controller: _controller,
        header: Container(
          color: Colors.white,
          child: ListTile(
            title: const Text("Header Panel"),
            trailing: _buildArrowIconHeader(),
            onTap: _controller.toggle,
          ),
        ),
        panel: (_) => _buildListPanel(),
        body: _buildGridList(),
      ),
      floatingActionButton: FloatingActionButton.extended(
        onPressed: _controller.toggle,
        tooltip: 'Toggle Panel',
        icon: const Icon(Icons.toggle_off),
        label: _buildTextFloatingButton(),
      ),
    );
  }

  Widget _buildArrowIconHeader() {
    return ValueListenableBuilder<bool>(
      valueListenable: _isPanelVisible,
      builder: (BuildContext _, bool isVisible, Widget? __) {
        return Icon(
          isVisible
              ? Icons.keyboard_arrow_up_rounded
              : Icons.keyboard_arrow_down_rounded,
          size: 20,
          color: Colors.black45,
        );
      },
    );
  }

  Widget _buildTextFloatingButton() {
    return ValueListenableBuilder<bool>(
      valueListenable: _isPanelVisible,
      builder: (BuildContext _, bool isVisible, Widget? __) {
        return Text(isVisible ? 'Close Panel' : 'Open Panel');
      },
    );
  }

  Widget _buildGridList() => GridView.builder(
        gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
          crossAxisCount: 2,
        ),
        itemBuilder: (BuildContext _, int __) => Container(
          color: _getColor(),
        ),
      );

  Widget _buildListPanel() => ListView.builder(
        itemCount: 20,
        padding: EdgeInsets.zero,
        itemBuilder: (BuildContext context, int index) => ListTile(
          title: Text("Item $index"),
          onTap: _controller.close,
        ),
      );

  Color _getColor() => Color.fromRGBO(
        math.Random().nextInt(255),
        math.Random().nextInt(255),
        math.Random().nextInt(255),
        1,
      );
}

说明

  1. 导入依赖

    import 'dart:math' as math;
    import 'package:flutter/material.dart';
    import 'package:sliding_top_panel/sliding_top_panel.dart';
    
  2. 创建 MyApp

    class MyApp extends StatelessWidget {
      const MyApp({Key? key}) : super(key: key);
    
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          title: 'Flutter Demo',
          theme: ThemeData(
            primarySwatch: Colors.blue,
          ),
          debugShowCheckedModeBanner: false,
          home: const MyHomePage(title: 'Example Sliding Panel'),
        );
      }
    }
    
  3. 创建 MyHomePage

    class MyHomePage extends StatefulWidget {
      const MyHomePage({Key? key, required this.title}) : super(key: key);
    
      final String title;
    
      @override
      State<MyHomePage> createState() => _MyHomePageState();
    }
    
  4. 创建 _MyHomePageState

    class _MyHomePageState extends State<MyHomePage> {
      final ValueNotifier<bool> _isPanelVisible = ValueNotifier(false);
      final SlidingPanelTopController _controller = SlidingPanelTopController();
    
      @override
      void initState() {
        super.initState();
        _controller.addListener(listenerController);
      }
    
      void listenerController() {
        _isPanelVisible.value = _controller.isPanelOpen;
      }
    
      @override
      void dispose() {
        _controller.removeListener(listenerController);
        _controller.dispose();
        super.dispose();
      }
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: Text(widget.title),
          ),
          body: SlidingTopPanel(
            decorationPanel: const BoxDecoration(
              color: Colors.white,
              borderRadius: BorderRadius.only(
                bottomLeft: Radius.circular(10),
                bottomRight: Radius.circular(10),
              ),
            ),
            controller: _controller,
            header: Container(
              color: Colors.white,
              child: ListTile(
                title: const Text("Header Panel"),
                trailing: _buildArrowIconHeader(),
                onTap: _controller.toggle,
              ),
            ),
            panel: (_) => _buildListPanel(),
            body: _buildGridList(),
          ),
          floatingActionButton: FloatingActionButton.extended(
            onPressed: _controller.toggle,
            tooltip: 'Toggle Panel',
            icon: const Icon(Icons.toggle_off),
            label: _buildTextFloatingButton(),
          ),
        );
      }
    
      Widget _buildArrowIconHeader() {
        return ValueListenableBuilder<bool>(
          valueListenable: _isPanelVisible,
          builder: (BuildContext _, bool isVisible, Widget? __) {
            return Icon(
              isVisible
                  ? Icons.keyboard_arrow_up_rounded
                  : Icons.keyboard_arrow_down_rounded,
              size: 20,
              color: Colors.black45,
            );
          },
        );
      }
    
      Widget _buildTextFloatingButton() {
        return ValueListenableBuilder<bool>(
          valueListenable: _isPanelVisible,
          builder: (BuildContext _, bool isVisible, Widget? __) {
            return Text(isVisible ? 'Close Panel' : 'Open Panel');
          },
        );
      }
    
      Widget _buildGridList() => GridView.builder(
            gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
              crossAxisCount: 2,
            ),
            itemBuilder: (BuildContext _, int __) => Container(
              color: _getColor(),
            ),
          );
    
      Widget _buildListPanel() => ListView.builder(
            itemCount: 20,
            padding: EdgeInsets.zero,
            itemBuilder: (BuildContext context, int index) => ListTile(
              title: Text("Item $index"),
              onTap: _controller.close,
            ),
          );
    
      Color _getColor() => Color.fromRGBO(
            math.Random().nextInt(255),
            math.Random().nextInt(255),
            math.Random().nextInt(255),
            1,
          );
    }
    

运行效果

运行上述代码后,你将看到一个包含可滑动顶部面板的页面。顶部面板可以通过点击列表项的头部或浮动按钮来打开和关闭。面板打开时,浮动按钮的文本会变为 “Close Panel”,图标也会相应变化。

希望这个示例对你有所帮助!如果你有任何问题,欢迎随时提问。


更多关于Flutter滑动顶部面板插件sliding_top_panel的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter滑动顶部面板插件sliding_top_panel的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,以下是如何在Flutter中使用sliding_top_panel插件的一个基本示例。这个插件允许你实现一个可以滑动的顶部面板效果。首先,确保你已经在pubspec.yaml文件中添加了sliding_top_panel依赖:

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

然后,运行flutter pub get来安装依赖。

接下来是一个完整的示例代码,展示了如何使用sliding_top_panel插件:

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

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: HomeScreen(),
    );
  }
}

class HomeScreen extends StatefulWidget {
  @override
  _HomeScreenState createState() => _HomeScreenState();
}

class _HomeScreenState extends State<HomeScreen> with SingleTickerProviderStateMixin {
  late AnimationController _controller;
  late Animation<double> _animation;

  @override
  void initState() {
    super.initState();
    _controller = AnimationController(
      duration: const Duration(milliseconds: 300),
      vsync: this,
    )..addListener(() {
        setState(() {});
      });

    _animation = CurvedAnimation(
      parent: _controller,
      curve: Curves.easeInOut,
    );
  }

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

  void _togglePanel() {
    setState(() {
      if (_controller.isCompleted) {
        _controller.reverse();
      } else {
        _controller.forward();
      }
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Sliding Top Panel Example'),
      ),
      body: Stack(
        children: [
          // 主体内容
          Container(
            color: Colors.grey[200],
            child: Center(
              child: Column(
                mainAxisAlignment: MainAxisAlignment.center,
                children: [
                  Text(
                    'Main Content',
                    style: TextStyle(fontSize: 24),
                  ),
                  SizedBox(height: 20),
                  ElevatedButton(
                    onPressed: _togglePanel,
                    child: Text('Toggle Panel'),
                  ),
                ],
              ),
            ),
          ),

          // 滑动顶部面板
          SlidingUpPanel(
            panel: Center(
              child: Container(
                color: Colors.blue,
                height: 200,
                child: Center(
                  child: Text(
                    'This is the sliding panel!',
                    style: TextStyle(color: Colors.white, fontSize: 20),
                  ),
                ),
              ),
            ),
            body: Container(), // 这里的body可以是空的,因为面板已经覆盖了它
            collapsedHeight: 50,
            borderRadius: BorderRadius.circular(16),
            panelSnapSize: 200,
            animationController: _controller,
            animationCurve: Curves.easeInOut,
            backdropEnabled: true,
            parallaxOffset: _animation,
            onDragEnd: (draggedHeight) {
              if (draggedHeight > _controller.value * _controller.upperBound) {
                _controller.forward();
              } else {
                _controller.reverse();
              }
            },
          ),
        ],
      ),
    );
  }
}

在这个示例中,我们创建了一个SlidingUpPanel,它包含一个蓝色的顶部面板和主体内容。当用户点击按钮时,顶部面板会向上滑动(如果它当前是关闭的)或向下滑动(如果它当前是打开的)。

  • panel参数定义了滑动面板的内容。
  • collapsedHeight定义了面板折叠时的高度。
  • borderRadius定义了面板的圆角。
  • panelSnapSize定义了面板完全展开时的高度。
  • animationControlleranimationCurve用于控制动画效果。
  • onDragEnd回调用于处理拖动结束时的逻辑,确保面板根据拖动的高度正确展开或折叠。

请注意,SlidingUpPanel组件的一些参数和用法可能会根据插件的更新而有所变化,因此请参考插件的官方文档以获取最新的信息和最佳实践。

回到顶部