Flutter数据获取插件fetcher的使用

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

Flutter数据获取插件fetcher的使用

fetcher 是一个用于处理异步任务(如网络请求)UI状态(加载、错误和数据)的Flutter插件。它遵循KISS原则,提供了简单易用的API。

主要功能

  • Minimalist library: 大部分使用原生Flutter组件和逻辑。
  • Ready to use: 提供默认小部件。
  • 基本使用非常简单直接,同时支持高级用法。
  • 全局配置与本地覆盖
  • 错误和重试处理,带有常见的用户体验行为。
  • 可以连接到错误报告服务
  • 在状态之间进行淡入淡出过渡以允许平滑的UI
  • 可选组件与BLoC模式一起使用(推荐)。

主要小部件

FetchBuilder

用于获取然后显示数据的小部件,处理加载、错误和数据状态,并包含重试系统。

SubmitBuilder

用于提交数据的小部件,处理加载和错误状态,并在加载时显示屏障以防止用户交互。

其他小部件

  • EventFetchBuilder: 监听EventStream并显示数据。
  • PagedListViewFetcher: 带有无限滚动的分页版本的FetchBuilder
  • SubmitFormBuilder: 提交数据并自动表单验证。
  • AsyncEditBuilder: 获取然后显示数据,并在需要时提交更改。

使用示例

获取数据

FetchBuilder<Weather>(
  task: api.getWeather,
  builder: (context, weather) => Text('Weather: ${weather.temperature}')
)

其中getWeather是一个返回Future<Weather>的异步函数。

自定义配置

FetchBuilder<Weather>(
  task: api.getWeather,
  config: FetcherConfig(
    fetchingBuilder: (context) => const CircularProgressIndicator(),
  ),
  builder: (context, weather) => Text('Weather: ${weather.temperature}')
)

提交数据

SubmitBuilder<void>(
  task: () => api.submitData('new data value'),
  onSuccess: (_) => Navigator.pop(context),
  builder: (context, runTask) => ElevatedButton(
    onPressed: runTask,
    child: const Text('Submit'),
  ),
)

完整示例:新闻阅读器应用

1. 获取数据

首先,构建bloc并添加方法从服务器获取最新文章:

import 'package:fetcher/fetcher_bloc.dart';

class NewsReaderBloc with Disposable {
  Future<NewsArticle> fetchArticle() async {
    await Future.delayed(const Duration(seconds: 2));
    return NewsArticle('Title 1', 'Random content generated for page 1, at ${DateTime.now()}');
  }
}

class NewsArticle {
  const NewsArticle(this.title, this.content);
  final String title;
  final String content;
}

在UI方面,创建一个新的stateful widget来持有bloc实例:

import 'package:fetcher/fetcher_bloc.dart';
import 'package:flutter/material.dart';

import 'news_reader.bloc.dart';

class NewsReaderPage extends StatefulWidget {
  const NewsReaderPage({super.key});

  @override
  State<NewsReaderPage> createState() => _NewsReaderPageState();
}

class _NewsReaderPageState extends State<NewsReaderPage> with BlocProvider<NewsReaderPage, NewsReaderBloc> {
  @override
  NewsReaderBloc initBloc() => NewsReaderBloc();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('News reader #1'),
      ),
      body: FetchBuilder<NewsArticle>(
        task: bloc.fetchArticle,
        builder: (context, article) {
          return Padding(
            padding: const EdgeInsets.all(20),
            child: Column(
              children: [
                Text(article.title, style: Theme.of(context).textTheme.headlineSmall),
                const SizedBox(height: 15),
                Text(article.content, style: Theme.of(context).textTheme.bodyLarge),
              ],
            ),
          );
        }
      ),
    );
  }
}

2. 动态小部件

为了处理用户交互并更新界面,可以使用DataStreamDataStreamBuilder

enum ArticleVote { like, dislike }

class NewsReaderBloc with Disposable {
  final selectedVote = DataStream<ArticleVote?>(null);

  @override
  void dispose() {
    selectedVote.close();
    super.dispose();
  }

  void selectVote(ArticleVote vote) => selectedVote.add(vote, skipSame: true);
}

// UI side
DataStreamBuilder<ArticleVote?>(
  stream: bloc.selectedVote,
  builder: (context, selectedVote) {
    return ToggleButtons(
      isSelected: [
        selectedVote == ArticleVote.like,
        selectedVote == ArticleVote.dislike,
      ],
      onPressed: (index) => bloc.selectVote(index == 0 ? ArticleVote.like : ArticleVote.dislike),
      children: const [Icon(Icons.thumb_up), Icon(Icons.thumb_down)],
    );
  },
)

3. 提交数据

使用SubmitBuilder提交用户的投票:

Future<ArticleVote> voteArticle([ArticleVote? vote]) async {
  vote ??= selectedVote.value;
  if (vote == null) throw Exception('Select a vote first');
  await Future.delayed(const Duration(seconds: 1));
  return vote;
}

Widget build(BuildContext context) {
  return SubmitBuilder<ArticleVote>(
    task: bloc.voteArticle,
    onSuccess: (vote) {
      ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text('Voted successfully: ${vote.name}'), backgroundColor: Colors.green));
      Navigator.of(context).pushReplacement(MaterialPageRoute(builder: (context) => NewsReaderPage()));
    },
    builder: (context, runTask) {
      return ElevatedButton(onPressed: runTask, child: const Text('Vote'));
    }
  );
}

开始使用

  1. pubspec.yaml中添加依赖:
    dependencies:
      fetcher: ^latest_version
    
  2. 导入包:
    import 'package:fetcher/fetcher.dart';
    // 或者使用 BLoC 模式
    import 'package:fetcher/fetcher_bloc.dart';
    

更多详细信息和完整示例代码请参考官方GitHub仓库


更多关于Flutter数据获取插件fetcher的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter数据获取插件fetcher的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,下面是一个关于如何使用Flutter数据获取插件fetcher的示例代码。fetcher是一个假设的数据获取插件,用于演示目的,实际中可能需要根据具体插件的文档进行调整。

首先,确保在pubspec.yaml文件中添加fetcher依赖项(假设该插件存在):

dependencies:
  flutter:
    sdk: flutter
  fetcher: ^x.y.z  # 替换为实际的版本号

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

接下来,在Flutter应用中使用fetcher插件进行数据获取。以下是一个简单的示例,展示如何配置和使用fetcher来获取数据:

import 'package:flutter/material.dart';
import 'package:fetcher/fetcher.dart';  // 假设fetcher插件的导入路径

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

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

class DataFetcherScreen extends StatefulWidget {
  @override
  _DataFetcherScreenState createState() => _DataFetcherScreenState();
}

class _DataFetcherScreenState extends State<DataFetcherScreen> {
  Fetcher? _fetcher;
  String? _data;
  bool _isLoading = false;

  @override
  void initState() {
    super.initState();
    _initializeFetcher();
  }

  void _initializeFetcher() {
    // 假设Fetcher有一个配置和初始化的方法
    _fetcher = Fetcher(
      endpoint: 'https://api.example.com/data',  // 替换为实际API端点
      onResponse: (response) {
        setState(() {
          _data = response.body;  // 假设response有一个body属性
          _isLoading = false;
        });
      },
      onError: (error) {
        setState(() {
          _data = 'Error: $error';
          _isLoading = false;
        });
      },
    );

    // 开始获取数据
    _fetchData();
  }

  void _fetchData() {
    setState(() {
      _isLoading = true;
    });
    _fetcher?.fetch();  // 假设Fetcher有一个fetch方法来获取数据
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Fetcher Demo'),
      ),
      body: Center(
        child: _isLoading
            ? CircularProgressIndicator()
            : Text(
                _data ?? 'No data yet',
                style: TextStyle(fontSize: 20),
              ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _fetchData,
        tooltip: 'Fetch Data',
        child: Icon(Icons.refresh),
      ),
    );
  }
}

在这个示例中:

  1. 依赖项:在pubspec.yaml文件中添加了fetcher依赖项。
  2. 配置Fetcher:在_initializeFetcher方法中配置了Fetcher实例,包括API端点、响应处理函数和错误处理函数。
  3. 获取数据:通过调用_fetcher?.fetch()方法开始数据获取,并在获取过程中显示加载指示器。
  4. UI展示:在UI中显示获取到的数据或错误信息,并提供一个刷新按钮来重新获取数据。

请注意,这个示例假设fetcher插件有一个类似Fetcher的类,并且该类具有endpointonResponseonErrorfetch等方法和属性。实际使用中,需要根据fetcher插件的实际API文档进行调整。

回到顶部