问题描述
我真的很扑朔迷离。 据我所知,Cubit是相当新的东西,现在它是BloC模式的基础。
我试图了解它的工作原理,并且了解一些关于状态管理的概念和知识,并且尝试构建一个简单的应用程序。
该应用程序与API相连,该API响应商店列表并具有BottomTabBar。
这是我的代码:
main.dart
import 'package:myapp/cubit/maison_cubit.dart';
import 'package:myapp/pages/maison.dart';
import 'package:myapp/pages/home.dart';
import 'package:myapp/repository/maisons_repository.dart';
import 'package:Flutter/foundation.dart';
import 'package:Flutter/material.dart';
import 'package:Flutter/services.dart';
import 'package:Flutter_bloc/Flutter_bloc.dart';
import 'package:google_fonts/google_fonts.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
final textTheme = Theme.of(context).textTheme;
return MaterialApp(
debugShowCheckedModeBanner: false,title: 'My Cubit app',home: BlocProvider(
create: (context) => MaisonCubit(MaisonsRepository()),child: HomePage(),),routes: {
MaisonPage.routeName: (ctx) => MaisonPage(),},);
}
}
我的maison_repository.dart
只需调用一个外部API,然后将其添加到列表中即可。
import 'dart:convert';
import 'package:myapp/const.dart';
import 'package:http/http.dart' as http;
import '../models/maison.dart';
class MaisonsRepository {
List<Maison> items = [];
Future<List<Maison>> getMaisons() async {
final response = await http.get('https://get-maison.example',headers: {
"Accept": "application/json","content-type": "application/json",});
if (response.statusCode == 200) {
items.clear();
List<dynamic> list = json.decode(response.body);
list.forEach((element) {
items.add(Maison.fromJson(element));
});
return items;
} else {
throw Exception('Failed to load maisons');
}
}
Maison find(String id) {
return items.firstWhere((element) => element.id == id);
}
}
这是home.dart
。在MaisonLoaded
中,我打电话给BottomBar
,这需要建立底部的栏并显示正确的页面。在我阅读的所有文档中,我从存储库中获取数据后都没有找到有关如何管理数据的很好的解释,因此我在BottomBar
中添加了构造函数,并传递了数据。正确吗?
import 'package:myapp/bottom_bar.dart';
import 'package:myapp/cubit/maison_cubit.dart';
import 'package:myapp/pages/maisons_listing.dart';
import 'package:Flutter/material.dart';
import 'package:Flutter_bloc/Flutter_bloc.dart';
class HomePage extends StatefulWidget {
@override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
@override
Widget build(BuildContext context) {
final maisonCubit = context.bloc<MaisonCubit>();
maisonCubit.getMaisons();
return BlocConsumer<MaisonCubit,MaisonState>(
listener: (context,state) {
if (state is MaisonError) {
return Container(
child: Text('Missing connection'),);
}
},builder: (context,state) {
if (state is MaisonLoading) {
return Container(
child: Text('My loader here'),);
}
if (state is MaisonLoaded) {
return Container(
child: BottomBar(state.maisons),);
}
return Container();
},);
}
}
这是BottomBar
小部件(MapPage
只是其中包含文本的容器)
import 'package:myapp/models/maison.dart';
import 'package:myapp/pages/maisons_listing.dart';
import 'package:myapp/pages/map.dart';
import 'package:Flutter/material.dart';
class BottomBar extends StatefulWidget {
final List<Maison> maisons;
BottomBar(this.maisons);
@override
_BottomBarState createState() => _BottomBarState();
}
class _BottomBarState extends State<BottomBar> {
int _currentIndex = 0;
void onTabTapped(int index) {
setState(() {
_currentIndex = index;
});
}
@override
Widget build(BuildContext context) {
List<Widget> _children = [
MaisonListingPage(widget.maisons),MapPage(widget.maisons),];
return Scaffold(
body: _children[_currentIndex],bottomNavigationBar: BottomNavigationBar(
backgroundColor: Color(0xFFDDCDC8),currentIndex: _currentIndex,onTap: onTabTapped,items: [
BottomNavigationBarItem(
icon: Icon(Icons.list),title: Text('Maisons'),BottomNavigationBarItem(
icon: Icon(
Icons.map,title: Text('Map'),],);
}
}
import 'package:myapp/models/maison.dart';
import 'package:myapp/pages/maison.dart';
import 'package:Flutter/material.dart';
class MaisonListingPage extends StatefulWidget {
final List<Maison> maisons;
MaisonListingPage(this.maisons);
@override
_MaisonListingPageState createState() => _MaisonListingPageState();
}
class _MaisonListingPageState extends State<MaisonListingPage> {
List<Maison> _currentList = [];
List<Maison> _maisons = [];
@override
Widget build(BuildContext context) {
_maisons = widget.maisons;
return SingleChildScrollView(
child: Column(
children: <Widget>[
HeroWidget(),Transform.translate(
offset: Offset(0,-30),child: Container(
height: 60.0,padding: EdgeInsets.symmetric(vertical: 5.0),margin: EdgeInsets.symmetric(horizontal: 16),decoration: Boxdecoration(
color: Colors.white,BoxShadow: [
BoxShadow(
color: Colors.grey[350],blurRadius: 20.0,offset: Offset(0,10.0),child: ListTile(
leading: Icon(Icons.search),title: TextField(
decoration: Inputdecoration(
border: InputBorder.none,hintText: AppLocalizations.of(context)
.translate('home_search_input_placeholder'),trailing: IconButton(
icon: Icon(
Icons.clear,onpressed: () {
},MediaQuery.removePadding(
context: context,removetop: true,child: maisonListView(),);
}
ListView maisonListView() {
return ListView.builder(
shrinkWrap: true,primary: false,itemCount: _currentList.length,itemBuilder: (BuildContext context,int index) {
return GestureDetector(
onTap: () => Navigator.of(context).pushNamed(
MaisonPage.routeName,arguments: _currentList[index].id,child: Text(_currentList[index].name),);
},);
}
onChange() {
setState(() {});
}
}
如果我运行代码,则可以看到大厦的列表。问题出在单个家具上,我想打开一个新页面并显示所有内容。
因此,我在maison_repository中添加了一个方法,如果您选中它,则可以看到一个find
方法。
在单个maison页面中,我试图初始化存储库并以这种方式使用find方法:
import 'dart:async';
import 'package:myapp/models/maison.dart';
import 'package:myapp/repository/maisons_repository.dart';
import 'package:Flutter/material.dart';
class MaisonPage extends StatefulWidget {
static const routeName = '/single-maison';
@override
_MaisonPageState createState() => _MaisonPageState();
}
class _MaisonPageState extends State<MaisonPage> {
MaisonsRepository _maisonsRepository;
@override
Widget build(BuildContext context) {
String maisonId = ModalRoute.of(context).settings.arguments as String;
Maison maison = _maisonsRepository.find(maisonId);
return Scaffold(
body: CustomScrollView(
slivers: <Widget>[
SliverAppBar(
expandedHeight: MediaQuery.of(context).size.width * 0.70,pinned: true,flexibleSpace: FlexibleSpaceBar(
title: Text(maison.name),background: MaisonHeroWidget(
id: maison.id,imageUrl: maison.imageUrl,SliverList(
delegate: SliverChildListDelegate(
[
Padding(
padding: const EdgeInsets.all(16.0),child: Column(
children: <Widget>[
Text(
maison.description,);
}
}
当我尝试访问此页面时,我得到:
The following NoSuchMethodError was thrown building MaisonPage(dirty,dependencies: [_ModalScopestatus],state: _MaisonPageState#335d9):
The method 'find' was called on null.
Receiver: null
Tried calling: find("2389")
The relevant error-causing widget was:
MaisonPage file:///Users/christiangiupponi/Dev/FlutterApp/myapp/lib/main.dart:63:40
When the exception was thrown,this was the stack:
#0 Object.noSuchMethod (dart:core-patch/object_patch.dart:53:5)
#1 _MaisonPageState.build (package:myapp/pages/maison.dart:69:40)
#2 StatefulElement.build (package:Flutter/src/widgets/framework.dart:4619:28)
#3 ComponentElement.performRebuild (package:Flutter/src/widgets/framework.dart:4502:15)
#4 StatefulElement.performRebuild (package:Flutter/src/widgets/framework.dart:4675:11)
...
════════════════════════════════════════════════════════════════════════════════════════════════════
解决方法
如评论中所述,_maisonsRepository
尚未初始化。对于空的 items
列表,您可以在存储库初始化后调用 getMaisons
。