使用 Hive DB Flutter 制作收藏夹页面的问题

Troubles with making a Favorite page with Hive DB Flutter

大家好,这是我的测试应用程序,我在制作收藏页面部分时遇到了一些问题,您可以在其中点击按钮并将项目添加到收藏页面。 我从 API 接收数据并通过 Listview.builder 实现它 以下是它应该是什么样子的一些照片: Home page

Favorite page

main.dart,我在这里打开一个名为 'favorites_box'

的框
import 'package:get/get.dart';
import 'package:get_storage/get_storage.dart';
import 'package:hive/hive.dart';
import 'package:hive_flutter/hive_flutter.dart';


void main() async{
  await GetStorage.init();
  await Hive.openBox('favorites_box');
  runApp(MainPage());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return GetMaterialApp(
      initialRoute: '/',
      getPages: [
        GetPage(name: '/', page: () => MyApp()),
        GetPage(name: '/main-page', page: () => MainPage()),
        GetPage(name: '/favorite_page', page: () => FavoritePage()),
        // Dynamic route
      ],
      home: MainPage(),
    );
  }
}

这是主页的代码: main_page.dart

import 'package:flutter/material.dart';
import '../View/listview_api.dart';
    
class MainPage extends StatefulWidget {
  @override
  State<MainPage> createState() => _MainPageState();
}

class _MainPageState extends State<MainPage> {
  int currentIndex = 0;

  List<BottomNavigationBarItem>? items;

  final screens = [
    HomePage(),
    HomePage()
    FavoritePage(),
    HomePage()
  ];

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      home: SafeArea(
        child: Scaffold(
          appBar: AppBar(
            backgroundColor: Colors.white,
            title: Container(
              width: double.infinity,
              height: 40,
              color: Colors.white,
              child: Center(
                child: TextField(
                  decoration: InputDecoration(
                    border: OutlineInputBorder(

                    ),
                      hintText: 'Searching',
                      prefixIcon: Icon(Icons.search),
                      suffixIcon: Icon(Icons.notifications)),
                ),
              ),
            ),
          ),
          body: screens[currentIndex],
          bottomNavigationBar: BottomNavigationBar(
          unselectedItemColor: Colors.grey,//AppColors.unselectedBottomNavItem,
          selectedItemColor: Colors.blue,//AppColors.assets,
          onTap: (index) => setState(() {
            currentIndex = index;
          }),//controller.setMenu(BottomMenu.values[pos]),
          //currentIndex: ,//controller.bottomMenu.index,
          type: BottomNavigationBarType.fixed,
          backgroundColor: Colors.white,
          currentIndex: currentIndex,
          selectedLabelStyle: const TextStyle(
            fontSize: 10,
            fontWeight: FontWeight.w500,
          ),
          unselectedLabelStyle: const TextStyle(
            fontSize: 10,
            fontWeight: FontWeight.w500,
          ),
          elevation: 8,
            items: [
              BottomNavigationBarItem(
                icon: Icon(Icons.home),
                label: 'Home',
                backgroundColor: Colors.blue,
              ),
              BottomNavigationBarItem(
                icon: Icon(Icons.add_shopping_cart),
                label: 'Shopping cart',
                backgroundColor: Colors.red,
              ),
              BottomNavigationBarItem(
                icon: Icon(Icons.favorite),
                label: 'Favorite',
                backgroundColor: Colors.green,
              ),
              BottomNavigationBarItem(
                icon: Icon(Icons.person),
                label: 'Profile',
                backgroundColor: Colors.yellow,
              ),
            ],
        ),
        ),
      ),
    );
  }
}

class HomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return SafeArea(
      child: SingleChildScrollView(
        scrollDirection: Axis.vertical,
        child: Center(
          child: Padding(
            padding: EdgeInsets.all(10.0),
            child: Column(
              mainAxisAlignment: MainAxisAlignment.spaceEvenly,
              children: [
                //Image.asset('images/image0.jpg'),
                SizedBox(
                  height: 25.0,
                ),
                Row(
                  mainAxisAlignment: MainAxisAlignment.spaceBetween,
                  children: [
                    Text(
                      'New!',
                      textAlign: TextAlign.left,
                      style: TextStyle(
                        fontSize: 25.0,
                        fontWeight: FontWeight.bold,
                      ),
                    ),
                    IconButton(
                      onPressed: () {},
                      icon: Icon(
                        Icons.arrow_forward_outlined,
                      ),
                    ),
                  ],
                ),
                SizedBox(
                  height: 25.0,
                ),
                SizedBox(
                  height: 300.0,
                  width: double.infinity,
                  child: ListViewAPI(),
                ),
              ],
            ),
          ),
        ),
      ),
    );
  }
}

现在,下面是 ListViewAPI() 的代码,这里我将点击的元素添加到框 ('favorites_box'):listview_api.dart

import 'package:flutter/material.dart';
import 'package:hive/hive.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';

String? stringResponse;
Map? mapResponse;
Map? dataResponse;
List? listResponse;

class ListViewAPI extends StatefulWidget {
  const ListViewAPI({Key? key}) : super(key: key);

  @override
  _ListViewAPIState createState() => _ListViewAPIState();
}

class _ListViewAPIState extends State<ListViewAPI> {
  Future apiCall() async {
    http.Response response;
    response = await http.get(Uri.parse("https://api.client.macbro.uz/v1/product"));
    if(response.statusCode == 200) {
      setState(() {
        //  stringResponse = response.body;
        mapResponse = jsonDecode(response.body);
        listResponse = mapResponse!['products'];
      });
    }
  }

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      home: Scrollbar(
          child: ListView.builder(
            scrollDirection: Axis.horizontal,
            itemBuilder: (context, index) {
              return Stack(
                children: [
                  Card(
                    child: Image.network(
                      listResponse![index]['image'],
                    ),
                  ),
                  Positioned(
                    right: 0,
                    child: InkWell(
                      child: IconButton(
                        onPressed: () async {
                          await Hive.box('favorites_box').put(listResponse![index]['image'], listResponse);
                        },
                        icon: Icon(
                          Icons.favorite_rounded,
                          color: Colors.red,
                        ),
                      ),
                    ),
                  ),
                ],
              );
            },
            itemCount: listResponse == null ? 0 : listResponse!.length,
          ),
        ),
    );
  }
}

所以在这里,我创建了一个列表,并尝试从名为“favorites_box”的框中保存元素,并获得了在我点击最喜欢的 IconButton upper 但没有成功时添加的数据(: favorite_page.dart

import 'package:flutter/material.dart';
import 'package:hive/hive.dart';
import 'package:hive_flutter/hive_flutter.dart';

import '../View/gridview_api.dart';

class FavoritePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {

    return Scaffold(
      body: ValueListenableBuilder(
        valueListenable: Hive.box('favorites_box').listenable(),
        builder: (context, box, child) {
         List posts = List.from(Hive.box('favorites_box').values);
          return ListView.builder(
            scrollDirection: Axis.horizontal,
            itemBuilder: (context, index) {
              return Column(
                children: [
                  Text(
                    'List of favorite products'
                  ),
                  Card(
                    child: posts[index] == null ? Text('nothing(') : posts[index],
                   // child: Hive.box('favorites_box').get(listResponse),
                  ),
                ],
              );
            },
          );
        },
      ),
    );
  }
}

如果有人能帮助我解决这个问题,我将不胜感激,因为我正在尝试解决这个问题几天 P.s。很抱歉给您带来不便,我是新手,所以希望您能理解我 谢谢!

我认为这不是您的代码的问题。但是,我确实建议为您的数据创建一个模型 class 并可能使用 FutureBuilder https://api.flutter.dev/flutter/widgets/FutureBuilder-class.html.

我认为问题在于您尚未更新 AndroidManifest.xml 文件以允许互联网连接。

尝试添加:

<uses-permission android:name="android.permission.INTERNET" />

到你的android\app\src\main\AndroidManifest.xml,上面

进一步阅读:https://flutter-examples.com/add-permissions-in-androidmanifest-xml-file/

仔细研究了你的问题后,我想我找到了问题所在。

Hive 需要初始化:

void main() async {
  // await GetStorage.init(); // Not sure why this was here but doesn't seem to be needed.
  await Hive.initFlutter();

  await Hive.openBox('favorites_box');
  runApp(MainPage());
}

您还遗漏了 main_page.dart

中的逗号
final screens = [
    HomePage(),
    HomePage() <----
    FavoritePage(),
    HomePage()
  ];

对于您的收藏夹页面,我将 ValueListenableBuilder 替换为 ListView.builder:

class FavoritePage extends StatelessWidget {
  List posts = List.from(Hive.box('favorites_box').values);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: ListView.builder(
        scrollDirection: Axis.horizontal,
        itemBuilder: (context, index) {
          return Stack(
            children: [
              Card(
                child: Image.network(
                  posts[index]['image'],
                ),
              ),
              Positioned(
                right: 0,
                child: InkWell(
                  child: IconButton(
                    onPressed: () async {
                      await Hive.box('favorites_box').delete(posts[index]);
                    },
                    icon: Icon(
                      Icons.favorite_rounded,
                      color: Colors.red,
                    ),
                  ),
                ),
              ),
            ],
          );
        },
        itemCount: posts == null ? 0 : posts.length,
      ),
    );
  }
}

当您尝试使用这个 posts[index]['image'] type 'String' is not a subtype of type 'int' of 'index' 时仍然会出现错误,但您可以通过创建模型 class 并访问具有这些属性的所有内容来轻松解决此问题。 Using model class in flutter (dart) 这是一个模型示例 class。您可以添加 toList() 或 toMap() 方法,而不是使用 DocumentSnapshot。

希望这对您有所帮助。它在我的模拟器上工作。 但我只是打印出完整的字符串,而不是使用卡片中的图像 child。

示例模型 Class:

import 'dart:convert';

void main() async {
  String data = '{"id":"626694d4f1ce2a0012f0fe1c","name":"JBL Party Box On-The-Go","slug":"jbl-party-box-on-the-go-juqgil2ep8ult","active":true,"image":"https://cdn.macbro.uz/macbro/1fad4f47-51f4-4f12-975b-657d780c98af","code":"","order":"0","cheapest_price":0,"price":{"price":520,"old_price":0,"uzs_price":5994000,"second_price":0,"second_uzs_price":7012500},"discount":0}';
  
  var test = new ProductModel.fromJson(json.decode(data));
  print(test.image);
}
  
class ProductModel {
  String? name;
  String? image;
  
  
  ProductModel.fromJson(Map json) {
    this.name = json['id'];
    this.image = json['image'];
  }
  
}

好的。我现在有一个解决方案。它比您开始使用的要复杂一些,但它在测试期间有效。

我使用 https://marketplace.visualstudio.com/items?itemName=hirantha.json-to-dart 从 API 数据 JSON 创建了一个模型 class。一张用于产品,一张用于产品内部的价格图。

product_model.dart 导入 'dart:convert';

import 'package:equatable/equatable.dart';
import 'package:hive_flutter/hive_flutter.dart';

import 'price.dart';

part 'product_model.g.dart';

@HiveType(typeId: 1)
class ProductModel extends Equatable {
  @HiveField(0)
  final String? id;
  @HiveField(1)
  final String? name;
  @HiveField(2)
  final String? slug;
  @HiveField(3)
  final bool? active;
  @HiveField(4)
  final String? image;
  @HiveField(5)
  final String? code;
  @HiveField(6)
  final String? order;
  @HiveField(7)
  final int? cheapestPrice;
  @HiveField(8)
  final Price? price;
  @HiveField(9)
  final int? discount;

  const ProductModel({
    this.id,
    this.name,
    this.slug,
    this.active,
    this.image,
    this.code,
    this.order,
    this.cheapestPrice,
    this.price,
    this.discount,
  });

  factory ProductModel.fromMap(Map<String, dynamic> data) => ProductModel(
        id: data['id'] as String?,
        name: data['name'] as String?,
        slug: data['slug'] as String?,
        active: data['active'] as bool?,
        image: data['image'] as String?,
        code: data['code'] as String?,
        order: data['order'] as String?,
        cheapestPrice: data['cheapest_price'] as int?,
        price: data['price'] == null
            ? null
            : Price.fromMap(data['price'] as Map<String, dynamic>),
        discount: data['discount'] as int?,
      );

  Map<String, dynamic> toMap() => {
        'id': id,
        'name': name,
        'slug': slug,
        'active': active,
        'image': image,
        'code': code,
        'order': order,
        'cheapest_price': cheapestPrice,
        'price': price?.toMap(),
        'discount': discount,
      };

  /// `dart:convert`
  ///
  /// Parses the string and returns the resulting Json object as [ProductModel].
  factory ProductModel.fromJson(String data) {
    return ProductModel.fromMap(json.decode(data) as Map<String, dynamic>);
  }

  /// `dart:convert`
  ///
  /// Converts [ProductModel] to a JSON string.
  String toJson() => json.encode(toMap());

  ProductModel copyWith({
    String? id,
    String? name,
    String? slug,
    bool? active,
    String? image,
    String? code,
    String? order,
    int? cheapestPrice,
    Price? price,
    int? discount,
  }) {
    return ProductModel(
      id: id ?? this.id,
      name: name ?? this.name,
      slug: slug ?? this.slug,
      active: active ?? this.active,
      image: image ?? this.image,
      code: code ?? this.code,
      order: order ?? this.order,
      cheapestPrice: cheapestPrice ?? this.cheapestPrice,
      price: price ?? this.price,
      discount: discount ?? this.discount,
    );
  }

  @override
  bool get stringify => true;

  @override
  List<Object?> get props {
    return [
      id,
      name,
      slug,
      active,
      image,
      code,
      order,
      cheapestPrice,
      price,
      discount,
    ];
  }
}

price.dart 导入 'dart:convert';

import 'package:equatable/equatable.dart';
import 'package:hive_flutter/hive_flutter.dart';

part 'price.g.dart';

@HiveType(typeId: 2)
class Price extends Equatable {
  @HiveField(0)
  final int? price;
  @HiveField(1)
  final int? oldPrice;
  @HiveField(2)
  final int? uzsPrice;
  @HiveField(3)
  final int? secondPrice;
  @HiveField(4)
  final int? secondUzsPrice;

  const Price({
    this.price,
    this.oldPrice,
    this.uzsPrice,
    this.secondPrice,
    this.secondUzsPrice,
  });

  factory Price.fromMap(Map<String, dynamic> data) => Price(
        price: data['price'] as int?,
        oldPrice: data['old_price'] as int?,
        uzsPrice: data['uzs_price'] as int?,
        secondPrice: data['second_price'] as int?,
        secondUzsPrice: data['second_uzs_price'] as int?,
      );

  Map<String, dynamic> toMap() => {
        'price': price,
        'old_price': oldPrice,
        'uzs_price': uzsPrice,
        'second_price': secondPrice,
        'second_uzs_price': secondUzsPrice,
      };

  /// `dart:convert`
  ///
  /// Parses the string and returns the resulting Json object as [Price].
  factory Price.fromJson(String data) {
    return Price.fromMap(json.decode(data) as Map<String, dynamic>);
  }

  /// `dart:convert`
  ///
  /// Converts [Price] to a JSON string.
  String toJson() => json.encode(toMap());

  Price copyWith({
    int? price,
    int? oldPrice,
    int? uzsPrice,
    int? secondPrice,
    int? secondUzsPrice,
  }) {
    return Price(
      price: price ?? this.price,
      oldPrice: oldPrice ?? this.oldPrice,
      uzsPrice: uzsPrice ?? this.uzsPrice,
      secondPrice: secondPrice ?? this.secondPrice,
      secondUzsPrice: secondUzsPrice ?? this.secondUzsPrice,
    );
  }

  @override
  bool get stringify => true;

  @override
  List<Object?> get props {
    return [
      price,
      oldPrice,
      uzsPrice,
      secondPrice,
      secondUzsPrice,
    ];
  }
}

然后我使用 https://docs.hivedb.dev/#/custom-objects/generate_adapter 为这两个创建适配器。您可以阅读文档以了解如何使用 build_runner 和 hive_generator 包完成此操作。

在 main.dart 中,我注册了两个适配器并使用 product_model.dart 中的 ProductModel 类型打开了一个框。 main.dart

import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:hive_flutter/hive_flutter.dart';
import 'package:test/product_model/price.dart';
import 'package:test/product_model/product_model.dart';

import 'favorite_page.dart';
import 'homepage.dart';

void main() async {
  // await GetStorage.init();
  await Hive.initFlutter();
  Hive.registerAdapter(PriceAdapter());

  Hive.registerAdapter(ProductModelAdapter());

  await Hive.openBox<ProductModel>('favorites_box');
  runApp(MainPage());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return GetMaterialApp(
      initialRoute: '/',
      getPages: [
        GetPage(name: '/', page: () => MyApp()),
        GetPage(name: '/main-page', page: () => MainPage()),
        GetPage(name: '/favorite_page', page: () => FavoritePage()),
        // Dynamic route
      ],
      home: MainPage(),
    );
  }
}

listview_api.dart 除了将产品从 listResponse 映射到 ProductModel 对象外,大部分是相同的。

import 'package:flutter/material.dart';
import 'package:hive/hive.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';

import 'package:test/product_model/product_model.dart';

String? stringResponse;
Map? mapResponse;
Map? dataResponse;
List? listResponse;

class ListViewAPI extends StatefulWidget {
  const ListViewAPI({Key? key}) : super(key: key);

  @override
  _ListViewAPIState createState() => _ListViewAPIState();
}

class _ListViewAPIState extends State<ListViewAPI> {
  Future apiCall() async {
    http.Response response;
    response =
        await http.get(Uri.parse("https://api.client.macbro.uz/v1/product"));
    if (response.statusCode == 200) {
      setState(() {
        //  stringResponse = response.body;
        mapResponse = jsonDecode(response.body);

        listResponse = mapResponse!['products'];
        listResponse =
            listResponse!.map((e) => ProductModel.fromMap(e)).toList(); // Map all of the products in listResponse to a ProductModel object.
      });
    }
  }

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      home: Scrollbar(
        child: ListView.builder(
          scrollDirection: Axis.horizontal,
          itemBuilder: (context, index) {
            return Stack(
              children: [
                Card(
                  child: Image.network(
                    listResponse![index].image!,
                  ),
                ),
                Positioned(
                  right: 0,
                  child: InkWell(
                    child: IconButton(
                      onPressed: () async {
                        await Hive.box<ProductModel>('favorites_box').put(
                            listResponse![index].image, listResponse![index]);
                      },
                      icon: Icon(
                        Icons.favorite_rounded,
                        color: Colors.red,
                      ),
                    ),
                  ),
                ),
              ],
            );
          },
          itemCount: listResponse == null ? 0 : listResponse!.length,
        ),
      ),
    );
  }
}

homepage.dart不变。

favorite_page.dart 已更改为有状态小部件,然后在初始化时获取框值。

import 'package:flutter/material.dart';
import 'package:hive/hive.dart';
import 'package:hive_flutter/hive_flutter.dart';
import 'package:test/product_model/product_model.dart';

class FavoritePage extends StatefulWidget {
  @override
  State<FavoritePage> createState() => _FavoritePageState();
}

class _FavoritePageState extends State<FavoritePage> {
  var posts;
  @override
  void initState() {
    super.initState();
    posts = Hive.box<ProductModel>('favorites_box').values.toList();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: ListView.builder(
        scrollDirection: Axis.horizontal,
        itemBuilder: (context, index) {
          return Stack(
            children: [
              Card(
                child: Image.network(
                  posts[index].image!,
                ),
              ),
              Positioned(
                right: 0,
                child: InkWell(
                  child: IconButton(
                    onPressed: () async {
                      await Hive.box<ProductModel>('favorites_box')
                          .delete(posts[index]);
                    },
                    icon: Icon(
                      Icons.favorite_rounded,
                      color: Colors.red,
                    ),
                  ),
                ),
              ),
            ],
          );
        },
        itemCount: posts == null ? 0 : posts.length,
      ),
    );
  }
}

我真的鼓励您阅读有关 Hive 的文档,因为它包含大量信息。使用 hive 编码时的另一个提示是确保您定期清除模拟器或物理设备的存储和缓存。我在处理 Hive 中的错误时遇到了太多令人头疼的问题,仅仅是因为我忘记清除存储和缓存,这导致尽管更改了我的源代码,但仍导致数据错误。