我尝试添加网格列表,同时添加第二个网格项目,发现以下错误:
Error: Could not find the correct Provider<Cities> above this PropertiesGrid Widget
To fix, please:
* Ensure the Provider<Cities> is an ancestor to this PropertiesGrid Widget
* Provide types to Provider<Cities>
* Provide types to Consumer<Cities>
* Provide types to Provider.of<Cities>()
* Always use package imports. Ex: `import 'package:my_app/my_code.dart';
* Ensure the correct `context` is being used.
If none of these solutions work, please file a bug at:
https://github.com/rrousselGit/provider/issues
我在下面的代码部分中发现我的Home Screen
中的问题:
Expanded(
child: FutureBuilder<bool>(
future: getData(),
builder: (BuildContext context,
AsyncSnapshot<bool> snapshot) {
if (!snapshot.hasData) {
return const SizedBox();
} else {
return ChangeNotifierProvider(
create: (context) => Properties(),
child: PropertiesGrid(_showOnlyFavorites),
);
}
},
),
),
所以这是我在Providers
,Screens
,Widgets
这种部分中划分的代码,如下图所示:
这是我的主屏幕:
import 'package:flutter/rendering.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import '../providers/properties.dart';
import '../providers/cities.dart';
import '../widgets/properties_grid.dart';
import '../app_theme.dart';
class MyHomePage extends StatefulWidget {
const MyHomePage({Key key}) : super(key: key);
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> with TickerProviderStateMixin {
int currentTab = 0;
ScrollController _scrollController = ScrollController();
bool _showBottomBar = true;
_scrollListener() {
if (_scrollController.position.userScrollDirection ==
ScrollDirection.reverse) {
setState(() {
_showBottomBar = false;
});
} else if (_scrollController.position.userScrollDirection ==
ScrollDirection.forward) {
setState(() {
_showBottomBar = true;
});
}
}
var _showOnlyFavorites = false;
AnimationController animationController;
bool multiple = true;
@override
void initState() {
animationController = AnimationController(
duration: const Duration(milliseconds: 2000), vsync: this);
_scrollController.addListener(_scrollListener);
super.initState();
}
Future<bool> getData() async {
await Future<dynamic>.delayed(const Duration(milliseconds: 0));
return true;
}
@override
void dispose() {
animationController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return DefaultTabController(
length: 6, // Added
initialIndex: 0,
child: Scaffold(
resizeToAvoidBottomPadding: false,
extendBody: true,
floatingActionButton: FloatingActionButton(
child: Icon(Icons.add),
onPressed: () {},
),
floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked,
bottomNavigationBar: AnimatedContainer(
duration: Duration(milliseconds: 500),
child: _showBottomBar
? BottomAppBar(
elevation: 0,
shape: CircularNotchedRectangle(),
notchMargin: 10,
child: Container(
height: 60,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
MaterialButton(
padding: EdgeInsets.all(0),
minWidth: 155,
onPressed: () {
setState(() {
// currentScreen =
// Chat(); // if user taps on this dashboard tab will be active
currentTab = 1;
});
},
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Icon(
Icons.home,
color: currentTab == 1
? Colors.blue
: Colors.grey,
),
Text(
'Home',
style: TextStyle(
color: currentTab == 1
? Colors.blue
: Colors.grey,
),
),
],
),
)
],
),
// Right Tab bar icons
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
MaterialButton(
padding: EdgeInsets.all(0),
minWidth: 60,
onPressed: () {
setState(() {
// currentScreen =
// Settings(); // if user taps on this dashboard tab will be active
currentTab = 3;
});
},
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Icon(
Icons.view_list,
color: currentTab == 3
? Colors.blue
: Colors.grey,
),
Text(
'Property List',
style: TextStyle(
color: currentTab == 3
? Colors.blue
: Colors.grey,
),
),
],
),
),
MaterialButton(
padding: EdgeInsets.all(0),
minWidth: 77,
onPressed: () {
setState(() {
// currentScreen =
// Settings(); // if user taps on this dashboard tab will be active
currentTab = 4;
});
},
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Icon(
Icons.location_searching,
color: currentTab == 4
? Colors.blue
: Colors.grey,
),
Text(
'Map',
style: TextStyle(
color: currentTab == 4
? Colors.blue
: Colors.grey,
),
),
],
),
),
],
)
],
),
),
)
: Container(
color: Colors.white,
width: MediaQuery.of(context).size.width,
),
),
backgroundColor: AppTheme.white,
body: Stack(
children: <Widget>[
FutureBuilder<bool>(
future: getData(),
builder: (BuildContext context, AsyncSnapshot<bool> snapshot) {
if (!snapshot.hasData) {
return const SizedBox();
} else {
return Padding(
padding: EdgeInsets.only(
top: MediaQuery.of(context).padding.top),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
appBar(),
tabBar(),
Expanded(
child: FutureBuilder<bool>(
future: getData(),
builder: (BuildContext context,
AsyncSnapshot<bool> snapshot) {
if (!snapshot.hasData) {
return const SizedBox();
} else {
return ChangeNotifierProvider(
create: (context) => Properties(),
child: PropertiesGrid(_showOnlyFavorites),
);
}
},
),
),
],
),
);
}
},
),
],
),
),
);
}
Widget appBar() {
return SizedBox(
height: AppBar().preferredSize.height,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Padding(
padding: const EdgeInsets.only(top: 8, left: 8),
child: Container(
width: AppBar().preferredSize.height - 8,
height: AppBar().preferredSize.height - 8,
),
),
Expanded(
child: Center(
child: Padding(
padding: const EdgeInsets.only(top: 4),
child:
Image.asset('assets/images/logo.png', fit: BoxFit.contain),
),
),
),
Padding(
padding: const EdgeInsets.only(top: 8, right: 8),
child: Container(
width: AppBar().preferredSize.height - 8,
height: AppBar().preferredSize.height - 8,
color: Colors.white,
child: Material(
color: Colors.transparent,
child: InkWell(
borderRadius:
BorderRadius.circular(AppBar().preferredSize.height),
child: Icon(
Icons.location_on,
color: AppTheme.dark_grey,
),
onTap: () {
setState(() {
multiple = !multiple;
});
},
),
),
),
),
],
),
);
}
Widget tabBar() {
return SizedBox(
height: AppBar().preferredSize.height,
child: TabBar(
isScrollable: true,
unselectedLabelColor: Colors.green,
labelColor: Colors.blue,
indicatorColor: Colors.blue,
labelStyle: TextStyle(fontSize: 15.0,fontStyle: FontStyle.italic),
tabs: [
Tab(
child: Text('All'),
),
Tab(
child: Text('Office'),
),
Tab(
child: Text('Commercial'),
),
Tab(
child: Text('Land'),
),
Tab(
child: Text('House/Villa'),
),
Tab(
child: Text('Apartement'),
),
],
),
);
}
}
这是Grid小部件的列表:
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import './province_item.dart';
import './property_item.dart';
import '../providers/cities.dart';
import '../providers/properties.dart';
// import '../providers/properties.dart';
class PropertiesGrid extends StatelessWidget {
final bool showFavs;
PropertiesGrid(this.showFavs);
@override
Widget build(BuildContext context) {
final propertiesData = Provider.of<Properties>(context);
final citiesData = Provider.of<Cities>(context);
// final productData = Provider.of<Cities>(context);
final properties = showFavs ? propertiesData.favoriteItems : propertiesData.items;
final cities = citiesData.items;
return Container(
color: Colors.transparent,
child: SingleChildScrollView(
child: Column(
children: [
Row(
children: <Widget>[
Padding(
padding: const EdgeInsets.all(6.0),
child: Align(
child: Text(
"Show All",
style: TextStyle(
fontSize: 16,
fontStyle: FontStyle.italic,
color: Colors.green),
),
alignment: Alignment.topLeft,
),
),
Container(
margin: const EdgeInsets.only(left: 95.0),
child: Padding(
padding: const EdgeInsets.all(6.0),
child: Align(
child: Text(
"Popular Properties",
style: TextStyle(
fontSize: 18,
fontStyle: FontStyle.italic,
color: Colors.black.withOpacity(0.8)),
),
alignment: Alignment.topRight,
),
),
),
],
),
SizedBox(
height: 250,
child: Column(
children: <Widget>[
Expanded(
child: GridView.builder(
padding: const EdgeInsets.all(10.0),
itemCount: properties.length,
itemBuilder: (ctx, i) => ChangeNotifierProvider.value(
// builder: (c) => products[i],
value: properties[i],
child: PropertyItem(
// products[i].id,
// products[i].title,
// products[i].imageUrl,
),
),
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 1,
childAspectRatio: 3 / 4,
crossAxisSpacing: 10,
mainAxisSpacing: 10,
),
scrollDirection: Axis.horizontal,
),
),
],
),
),
Row(
children: <Widget>[
Padding(
padding: const EdgeInsets.all(6.0),
child: Align(
child: Text(
"Show All",
style: TextStyle(
fontSize: 16,
fontStyle: FontStyle.italic,
color: Colors.green),
),
alignment: Alignment.topLeft,
),
),
Container(
margin: const EdgeInsets.only(left: 180.0),
child: Padding(
padding: const EdgeInsets.all(6.0),
child: Align(
child: Text(
"Province",
style: TextStyle(
fontSize: 18,
fontStyle: FontStyle.italic,
color: Colors.black.withOpacity(0.8)),
),
alignment: Alignment.topRight,
),
),
),
],
),
SizedBox(
height: 250,
child: Column(
children: <Widget>[
Expanded(
child: GridView.builder(
padding: const EdgeInsets.all(10.0),
itemCount: cities.length,
itemBuilder: (ctx, i) => ChangeNotifierProvider.value(
// builder: (c) => products[i],
value: cities[i],
child: ProvinceItem(
// products[i].id,
// products[i].title,
// products[i].imageUrl,
),
),
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 1,
childAspectRatio: 3 / 4,
crossAxisSpacing: 10,
mainAxisSpacing: 10,
),
scrollDirection: Axis.horizontal,
),
),
],
),
),
],
),
),
);
}
}
这是与小部件相关的省项目:
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import '../providers/city.dart';
class ProvinceItem extends StatelessWidget {
@override
Widget build(BuildContext context) {
final city = Provider.of<City>(context, listen: false);
return Padding(
padding: const EdgeInsets.all(8.0),
child: InkWell(
splashColor: Colors.transparent,
onTap: () => {},
child: Container(
decoration: BoxDecoration(
borderRadius: const BorderRadius.all(Radius.circular(16.0)),
boxShadow: <BoxShadow>[
BoxShadow(
color: Colors.grey.withOpacity(0.6),
offset: const Offset(2.5, 2.5),
blurRadius: 16,
),
],
),
margin: EdgeInsets.all(2),
child: ClipRRect(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(15),
topRight: Radius.circular(15),
bottomLeft: Radius.circular(15),
bottomRight: Radius.circular(15),
),
child: Image.asset(
city.cityImage,
fit: BoxFit.cover,
),
),
),
),
);
}
}
这是City
提供者:
import 'package:flutter/material.dart';
class City with ChangeNotifier {
final String cityId;
final String cityName;
final String cityImage;
City({
@required this.cityId,
@required this.cityName,
@required this.cityImage,
});
}
并将这个伪数据放入Provider中,就像下面的代码一样:
import '../providers/city.dart';
import 'package:flutter/material.dart';
// import './property.dart';
class Cities with ChangeNotifier {
List<City> _items = [
City(
cityId: 'city1',
cityName: 'Amman',
cityImage: 'assets/images/amman.png',
),
City(
cityId: 'city2',
cityName: 'Jerash',
cityImage: 'assets/images/amman.png',
),
City(
cityId: 'city3',
cityName: 'Karak',
cityImage: 'assets/images/amman.png',
),
City(
cityId: 'city4',
cityName: 'Aqaba',
cityImage: 'assets/images/amman.png',
),
City(
cityId: 'city5',
cityName: 'Zarqa',
cityImage: 'assets/images/amman.png',
),
City(
cityId: 'city6',
cityName: 'Madaba',
cityImage: 'assets/images/amman.png',
),
City(
cityId: 'city7',
cityName: 'Balqa',
cityImage: 'assets/images/amman.png',
),
City(
cityId: 'city8',
cityName: 'Mafraq',
cityImage: 'assets/images/amman.png',
),
City(
cityId: 'city9',
cityName: 'Maan',
cityImage: 'assets/images/amman.png',
),
City(
cityId: 'city10',
cityName: 'Ajloun',
cityImage: 'assets/images/amman.png',
),
City(
cityId: 'city11',
cityName: 'Irbid',
cityImage: 'assets/images/amman.png',
),
City(
cityId: 'city12',
cityName: 'Tafila',
cityImage: 'assets/images/amman.png',
),
];
List<City> get items {
return [..._items];
}
City findById(String cityId) {
return _items.firstWhere((cit) => cit.cityId == cityId);
}
}
很抱歉我添加了很多代码,我试图添加所有需要的信息...
请问是否有任何遗漏的信息,只是让我知道将其添加到问题中。.
这是DeBug Banner屏幕:
答案 0 :(得分:1)
哇,那是您发布的很多代码。我必须承认我没有非常彻底地研究它,但是我认为我有问题所在:正如错误消息所指出的那样,您并没有注入类型为Cities
在上下文树中构建PropertiesGrid
的点之上。
在PropertiesGrid
中有以下几行:
final propertiesData = Provider.of<Properties>(context);
final citiesData = Provider.of<Cities>(context);
您正在从上下文树中您上方的位置{em>请求一个Properties
实例和一个Cities
实例。如果您不满足此要求,提供商将无情地使您崩溃。事实上,您得到了:
(22371): Error: Could not find the correct Provider<Cities> above this PropertiesGrid Widget
所以..让我们看一下您的PropertiesGrid
小部件在首页中的位置:
if (!snapshot.hasData) {
return const SizedBox();
} else {
return ChangeNotifierProvider(
create: (context) => Properties(),
child: PropertiesGrid(_showOnlyFavorites),
);
}
有一个提供程序可以在Properties
上方正确插入一个PropertiesGrid
实例,但是没有一个提供Cities
实例的提供程序,因此您的小部件具有无法满足的依赖关系
为快速解决方案,建议您使用MultiProvider
注入两个依赖项
if (!snapshot.hasData) {
return const SizedBox();
} else {
return MultiProvider(
providers: [
ChangeNotifierProvider(create: (context) => Properties()),
ChangeNotifierProvider(create: (context) => Cities()),
],
child: PropertiesGrid(_showOnlyFavorites),
);
}
请注意,使用MultiProvider
并不是强制性的:您可以向这两个实例注入两个提供程序,即使在HomePage树的高度也不一样。只要在 PropertiesGrid
之上构建了这两个提供者,您就很好。
不要担心Provider最初似乎很难完全掌握。一旦掌握了它,它便成为一个功能强大的状态管理工具。我建议您看一些online article或教程以进行一些练习。