Flutter上下文提供插件build_context_provider的使用

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

Flutter上下文提供插件build_context_provider的使用

build_context_provider 是一个允许你在Flutter widgets之外运行需要 BuildContext 的函数的包。这使得在没有直接访问到 BuildContext 的地方(例如业务逻辑层或服务类)调用与UI交互的方法变得可能。下面我们将详细介绍如何安装、使用以及一些示例代码来帮助你更好地理解这个插件。

安装

要使用 build_context_provider,首先你需要将其添加到你的项目依赖中。然后,在应用的根部放置 ListenerThatRunsFunctionsWithBuildContext() 组件以确保所有需要 BuildContext 的操作都能正常工作。

return Stack(
  children: [
    child,
    const ListenerThatRunsFunctionsWithBuildContext(),
  ],
);

使用

你可以通过 BuildContextProvider.call() 方法包装任何你想执行且需要 BuildContext 的函数。这样做的好处是,这些函数可以在不需要 BuildContext 的地方被调用。

BuildContextProvider.call(
  (context) => Navigator.pushNamed(context, '/somewhere'),
);

示例

这里有一个完整的例子,展示了如何在一个不直接拥有 BuildContext 的地方导航到另一个页面:

class GoToProfilePage {
  static void call() {
    BuildContextProvider.call(
      (context) => Navigator.pushNamed(context, '/profile'),
    );
  }
}

// 这行代码可以在任何地方使用。如你所见,你不再受限于构建上下文。
GoToProfilePage.call();

如果你需要从函数返回值,可以通过包裹你的函数并捕获其结果来实现:

bool aValueToBeReturnedFromTheFunction = false;

bool functionThatReturnsValue(BuildContext context) {
  Navigator.of(context).pushNamed('/profile');
  return true;
}

void aFunctionThatCapturesReturnedValue(BuildContext context) {
  aValueToBeReturnedFromTheFunction = functionThatReturnsValue(context);
}

BuildContextProvider()(aFunctionThatCapturesReturnedValue);

expect(aValueToBeReturnedFromTheFunction, isTrue);

设计原则

  • 无外部库:此包尽量减少对外部库的依赖。
  • 单一职责:只做一件事,并且做好这件事。

完整示例代码

接下来是一个更全面的例子,它演示了如何在实际项目中使用 build_context_provider 包。此示例包括两个屏幕之间的导航和显示SnackBar的功能,而这些功能都是在没有直接访问到 BuildContext 的情况下完成的。

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

void _displaySnackbar() {
  BuildContextProvider()((context) {
    ScaffoldMessenger.of(context).showSnackBar(
      const SnackBar(
        content: _Snackbar(),
      ),
    );
  });
}

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      initialRoute: '/first',
      routes: {
        '/first': (context) => const _Layout(child: _FirstScreen()),
        '/second': (context) => const _Layout(child: _SecondScreen()),
      },
    );
  }
}

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

  @override
  Widget build(BuildContext context) {
    return const Center(
      child: Text('First screen'),
    );
  }
}

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

  @override
  Widget build(BuildContext context) {
    return const Center(
      child: Text('Second screen'),
    );
  }
}

class _Layout extends StatelessWidget {
  final Widget child;
  const _Layout({Key? key, required this.child}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    final buildContextProvider = BuildContextProvider();
    return Scaffold(
      body: SafeArea(
        child: Column(
          mainAxisSize: MainAxisSize.max,
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            child,
            const ListenerThatRunsFunctionsWithBuildContext(),
          ],
        ),
      ),
      persistentFooterButtons: [
        ElevatedButton(
          child: const Text('Go to first screen'),
          onPressed: () => buildContextProvider(
            (context) => Navigator.pushNamed(context, '/first'),
          ),
        ),
        ElevatedButton(
          child: const Text('Go to second screen'),
          onPressed: () => buildContextProvider(
            (context) => Navigator.pushNamed(context, '/second'),
          ),
        ),
      ],
      floatingActionButton: const FloatingActionButton(
        child: Icon(Icons.add),
        onPressed: _displaySnackbar,
      ),
    );
  }
}

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

  @override
  Widget build(BuildContext context) {
    return SizedBox(
      height: 100,
      child: Column(
        children: const [
          Text('If you see this snackbar then everything is working properly.'),
          Text(
            'Function that creates this snackbar was called without a build context.',
          )
        ],
      ),
    );
  }
}

void main(List<String> args) {
  runApp(const _App());
}

这段代码创建了一个简单的Flutter应用程序,其中包含两个屏幕(_FirstScreen_SecondScreen),并且可以通过底部按钮进行切换。此外,还有一个浮动按钮用于展示SnackBar,即使是在没有直接获取到 BuildContext 的地方也可以成功触发。


更多关于Flutter上下文提供插件build_context_provider的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter上下文提供插件build_context_provider的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


在Flutter中,build_context_provider 是一个有用的插件,它允许你在应用的不同部分之间共享和访问 BuildContext。这在某些场景下非常有用,比如当你需要从深层组件中访问导航栏或执行与UI相关的操作时。

以下是一个使用 build_context_provider 的基本示例,展示了如何设置和使用它来共享 BuildContext

1. 添加依赖

首先,你需要在 pubspec.yaml 文件中添加 build_context_provider 的依赖:

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

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

2. 设置 BuildContextProvider

在你的应用顶层(通常是 MyAppMaterialApp 组件中),设置 BuildContextProvider

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

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return BuildContextProvider(
      child: MaterialApp(
        title: 'Flutter Demo',
        theme: ThemeData(
          primarySwatch: Colors.blue,
        ),
        home: MyHomePage(),
      ),
    );
  }
}

3. 使用 BuildContext

现在,你可以在任何需要的地方使用 BuildContextProvider.of(context) 来获取 BuildContext。以下是一个简单的例子,展示如何在深层组件中使用它:

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

class MyHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Flutter Demo Home Page'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text(
              'You have pushed the button this many times:',
            ),
            Counter(),
            ElevatedButton(
              onPressed: () {
                // 使用 BuildContextProvider.of(context) 获取上下文并导航
                final navigator = BuildContextProvider.of(context).navigator;
                navigator.push(
                  MaterialPageRoute(builder: (context) => SecondPage()),
                );
              },
              child: Text('Go to Second Page'),
            ),
          ],
        ),
      ),
    );
  }
}

class Counter extends StatefulWidget {
  @override
  _CounterState createState() => _CounterState();
}

class _CounterState extends State<Counter> {
  int _counter = 0;

  void _incrementCounter() {
    setState(() {
      _counter++;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Text(
      '$_counter',
      style: Theme.of(context).textTheme.headline4,
    );
  }
}

class SecondPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    // 这里你也可以使用 BuildContextProvider.of(context) 获取上下文进行其他操作
    return Scaffold(
      appBar: AppBar(
        title: Text('Second Page'),
      ),
      body: Center(
        child: Text('This is the second page!'),
      ),
    );
  }
}

在这个例子中,我们在 MyApp 中设置了 BuildContextProvider,然后在 MyHomePage 的按钮点击事件中,通过 BuildContextProvider.of(context).navigator 获取了导航器并导航到了 SecondPage

请注意,BuildContextProvider 提供的 navigator 只是一个简单的示例,它可能并不是直接提供的属性。通常,你会通过其他方式(如全局状态管理或依赖注入)来共享和访问导航器或其他需要跨组件共享的对象。这个插件的核心功能是在整个应用树中共享 BuildContext,但你需要根据自己的需求来决定如何使用它。

回到顶部