You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Stateful Widget中实现Riverpod状态(API)?

在Stateful Widget中使用Riverpod的FutureProvider展示WordPress新闻数据

核心修改步骤

  • 将HomeScreen从普通StatefulWidget改为ConsumerStatefulWidget,对应State改为ConsumerState,让State类能获取WidgetRef监听Provider
  • 移除initState中手动初始化_postsFuture的代码,直接通过Riverpod监听newsProvider获取数据
  • 用Riverpod的AsyncValue处理加载、错误、数据就绪三种状态,替代原FutureBuilder

修改后的完整代码

news_provider.dart(保持原代码不变)

// news_provider.dart
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';

final String baseUrl = 'https://thewordpress.com/wp-json/wp/v2';
final newsProvider = FutureProvider<List<Berita>> ((ref)  async {
    // final response = await http.get(Uri.parse('$baseUrl/posts?_embed')); //ini asli
    final response = await http.get(Uri.parse('$baseUrl/posts?per_page=20&_embed'));

    if (response.statusCode == 200) {
      List<dynamic> jsonResponse = json.decode(response.body);
      return jsonResponse.map((post) => Berita.fromJson(post)).toList();
    } else {
      throw Exception('GAGAL MENGAMBIL BERITA TERBARU');
    }
  });

class Berita {
  final int id;
  final String title;
  final String content;
  final String excerpt;
  final String featuredImageUrl;
  final DateTime date;
  final String link;
  final List<dynamic> categories;

  Berita({
    required this.id,
    required this.title,
    required this.content,
    required this.excerpt,
    required this.featuredImageUrl,
    required this.date,
    required this.link,
    required this.categories
  });

  factory Berita.fromJson(Map<String, dynamic> json) {
    return Berita(
      id: json['id'],
      title: json['title']['rendered'],
      content: json['content']['rendered'],
      excerpt: json['excerpt']['rendered'],
      featuredImageUrl: json['_embedded']['wp:featuredmedia'] != null
          ? json['_embedded']['wp:featuredmedia'][0]['source_url']
          : 'https://via.placeholder.com/400x200', // Replace with default image URL
      date: DateTime.parse(json['date']),
      link: json['link'],
      categories: json['categories'],
    );
  }
}

修改后的HomeScreen代码

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

class HomeScreen extends ConsumerStatefulWidget {
  const HomeScreen({super.key});

  @override
  ConsumerState<HomeScreen> createState() => _HomeScreenState();
}

class _HomeScreenState extends ConsumerState<HomeScreen> {
  late Future<List<Category>> categories;

  @override
  void initState() {
    super.initState();
    categories = ApiService().fetchCategories();
  }

  @override
  Widget build(BuildContext context) {
    // 监听newsProvider,获取异步数据状态
    final newsAsyncValue = ref.watch(newsProvider);

    return Scaffold(
      appBar: AppBar(
        title: Row(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            const Text('Aplikasi'),
            Text(
              'Berita',
              style: TextStyle(color: Colors.blue, fontWeight: FontWeight.bold),
            ),
          ],
        ),
        actions: [
          IconButton(
            icon: const Icon(Icons.search, color: Colors.red),
            onPressed: () {
              Navigator.push(
                context,
                MaterialPageRoute(builder: (context) => const SearchNews()),
              );
            },
          ),
        ],
        centerTitle: true,
        elevation: 0.0,
      ),
      body: SingleChildScrollView(
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Padding(
              padding: const EdgeInsets.all(8.0),
              child: Text(
                'Kategori',
                style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
              ),
            ),
            _buildCategory(),
            const Padding(
              padding: EdgeInsets.only(top: 50.0, bottom: 20, left: 8),
              child: Text(
                'Berita Utama',
                style: TextStyle(
                  fontSize: 20,
                  fontWeight: FontWeight.bold,
                  color: Colors.redAccent,
                ),
              ),
            ),
            _buildCarousel(),
            GestureDetector(
              onTap: () {
                Navigator.push(
                  context,
                  MaterialPageRoute(builder: (context) => const SearchNews()),
                );
              },
              child: Row(
                children: [
                  const Padding(
                    padding: EdgeInsets.only(
                      top: 40.0,
                      bottom: 20,
                      left: 8,
                      right: 8,
                    ),
                    child: Text(
                      'Berita Terbaru',
                      style: TextStyle(
                        fontSize: 20,
                        fontWeight: FontWeight.bold,
                        color: Colors.orange,
                      ),
                    ),
                  ),
                  const Spacer(),
                  GestureDetector(
                    onTap: () {
                      Navigator.push(
                        context,
                        MaterialPageRoute(builder: (context) => const BeritaTerbaru()),
                      );
                    },
                    child: const Padding(
                      padding: EdgeInsets.only(
                        top: 40.0,
                        bottom: 20,
                        left: 8,
                        right: 8,
                      ),
                      child: Text(
                        "Lihat",
                        style: TextStyle(
                          fontSize: 15,
                          fontWeight: FontWeight.bold,
                          color: Colors.black45,
                        ),
                      ),
                    ),
                  ),
                ],
              ),
            ),
            // 处理新闻数据展示
            newsAsyncValue.when(
              loading: () => const Center(child: CircularProgressIndicator()),
              error: (error, stack) => Center(child: Text('加载失败: $error')),
              data: (beritaList) => _buildLatestNews(beritaList),
            ),
          ],
        ),
      ),
    );
  }

  Widget _buildCategory() {
    return SizedBox(
      height: 35,
      child: FutureBuilder<List<Category>>(
        future: categories,
        builder: (context, snapshot) {
          if (snapshot.hasData) {
            return SizedBox(
              height: 15,
              width: MediaQuery.of(context).size.width,
              child: ListView.builder(
                shrinkWrap: true,
                scrollDirection: Axis.horizontal,
                itemCount: snapshot.data!.length,
                itemBuilder: (context, index) {
                  return GestureDetector(
                    onTap: () {
                      Navigator.push(
                        context,
                        MaterialPageRoute(
                          builder: (context) => ListNewsScreen(
                            categoryId: snapshot.data![index].id,
                            categoryName: snapshot.data![index].name,
                          ),
                        ),
                      );
                    },
                    child: Container(
                      margin: const EdgeInsets.only(left: 10, right: 10),
                      height: 250,
                      width: MediaQuery.of(context).size.width / 3.5,
                      decoration: BoxDecoration(
                        color: Colors.blue,
                        borderRadius: BorderRadius.circular(10),
                      ),
                      child: Center(
                        child: Text(
                          snapshot.data![index].name,
                          style: const TextStyle(
                            fontWeight: FontWeight.bold,
                            color: Colors.white,
                          ),
                        ),
                      ),
                    ),
                  );
                },
              ),
            );
          } else if (snapshot.hasError) {
            return Center(child: Text('${snapshot.error}'));
          }
          return const Center(child: CircularProgressIndicator());
        },
      ),
    );
  }

  // 接收Berita列表参数,构建新闻列表
  Widget _buildLatestNews(List<Berita> beritaList) {
    return ListView.builder(
      shrinkWrap: true,
      physics: const NeverScrollableScrollPhysics(),
      itemCount: beritaList.length,
      itemBuilder: (context, index) {
        final berita = beritaList[index];
        return Card(
          margin: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              Image.network(berita.featuredImageUrl, fit: BoxFit.cover),
              Padding(
                padding: const EdgeInsets.all(8.0),
                child: Text(
                  berita.title,
                  style: const TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
                ),
              ),
              Padding(
                padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
                child: Text(
                  berita.excerpt.replaceAll(RegExp(r'<[^>]*>'), ''),
                  maxLines: 2,
                  overflow: TextOverflow.ellipsis,
                ),
              ),
            ],
          ),
        );
      },
    );
  }

  // 补充轮播图实现逻辑
  Widget _buildCarousel() {
    return const SizedBox(height: 200, child: Center(child: Text('轮播图区域')));
  }
}

// 补充原代码中用到的未定义类
class Category {
  final int id;
  final String name;
  Category({required this.id, required this.name});
}

class SearchNews extends StatelessWidget {
  const SearchNews({super.key});
  @override
  Widget build(BuildContext context) {
    return Scaffold(appBar: AppBar(title: const Text('搜索')),);
  }
}

class BeritaTerbaru extends StatelessWidget {
  const BeritaTerbaru({super.key});
  @override
  Widget build(BuildContext context) {
    return Scaffold(appBar: AppBar(title: const Text('所有最新新闻')),);
  }
}

class ListNewsScreen extends StatelessWidget {
  final int categoryId;
  final String categoryName;
  const ListNewsScreen({super.key, required this.categoryId, required this.categoryName});
  @override
  Widget build(BuildContext context) {
    return Scaffold(appBar: AppBar(title: Text(categoryName)),);
  }
}

class ApiService {
  Future<List<Category>> fetchCategories() async {
    return Future.value([Category(id: 1, name: '科技'), Category(id: 2, name: '体育')]);
  }
}

关键说明

  1. ConsumerStatefulWidget:Riverpod为StatefulWidget提供的适配类,让State类可通过ref监听Provider状态变化
  2. AsyncValue.when:Riverpod内置的状态处理方法,统一管理加载、错误、数据就绪三种状态,替代传统FutureBuilder
  3. 自动数据管理:无需手动维护Future对象,Riverpod会自动缓存数据,当Provider更新时UI自动重建

内容的提问来源于stack exchange,提问作者Roly Audi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 01:04:55