Уточнение о провайдере во Flutter - Как сохранить данные

Я новичок в Flutter и я трачу некоторое время на скороговорку поставщика, я парень PHP, поэтому у меня есть базовые знания о том, как это работает, особенно в Laravelфреймворк.
Для меня провайдер - это то, что должно управлять данными, извлекать их из БД, управлять сохранением рекламы.

Насколько я понимаю, во флаттере концепция такая же, но следовать документации довольно сложно.

Я создал простое приложение, которое вызывает REST API, и теперь я хотел бы добавить ответ своему провайдеру, чтобы использовать данные на всех моих страницах и виджетах.

Вот мой код.

В splash.dartфайл, который показывает экран-заставку моему пользователю и вызывает внешнюю конечную точку, здесь я хотел бы получить свои данные и добавить их к поставщику.

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

import 'models/maison.dart';
import 'dart:convert' as convert;
import 'package:http/http.dart' as http;

class SplashApp extends StatefulWidget {
  final VoidCallback onInitializationComplete;

  const SplashApp({
    Key key,
    @required this.onInitializationComplete,
  }) : super(key: key);

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

class _SplashAppState extends State<SplashApp> {
  @override
  void initState() {
    super.initState();
    _initializeAsyncDependencies();
  }

  Future<void> _initializeAsyncDependencies() async {
    var url = 'https://run.mocky.io/v3/95237af1-b13f-4756-b308-56c9aac93c7e';

    // Await the http get response, then decode the json-formatted response.
    var response = await http.get(url);
    if (response.statusCode == 200) {
      var jsonResponse = convert.jsonDecode(response.body);
      var data = jsonResponse['data'];
      var name = data[0]['name'];
      print('Name: $name.');
    } else {
      print('Request failed with status: ${response.statusCode}.');
    }
    // >>> initialize async dependencies <<<
    // >>> register favorite dependency manager <<<
    // >>> reap benefits <<<
    Future.delayed(
      Duration(milliseconds: 3000),
      () => widget.onInitializationComplete(),
    );
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      title: 'Splash Screen',
      theme: ThemeData(
        accentColor: Colors.black,
      ),
      home: _buildBody(),
    );
  }

  Widget _buildBody() {
    return Scaffold(
      body: Stack(
        fit: StackFit.expand,
        children: <Widget>[
          Container(
            decoration: BoxDecoration(
              color: Color(0xFFDDCDC8),
            ),
          ),
          Column(
            mainAxisAlignment: MainAxisAlignment.start,
            children: <Widget>[
              Expanded(
                flex: 2,
                child: Container(
                  child: Column(
                    mainAxisAlignment: MainAxisAlignment.center,
                    children: <Widget>[
                      CircleAvatar(
                        backgroundColor: Colors.white,
                        radius: 80.0,
                        child: Icon(
                          Icons.access_time,
                          color: Colors.black,
                          size: 80.0,
                        ),
                      ),
                      Padding(
                        padding: EdgeInsets.only(
                          top: 10.0,
                        ),
                      ),
                      Text(
                        'Title',
                        style: TextStyle(
                          fontWeight: FontWeight.bold,
                          fontSize: 26.0,
                        ),
                      ),
                      SizedBox(
                        height: 16.0,
                      ),
                      Text(
                        'Subtitle',
                        style: TextStyle(
                          fontSize: 16.0,
                        ),
                      ),
                    ],
                  ),
                ),
              ),
              Expanded(
                flex: 1,
                child: Column(
                  mainAxisAlignment: MainAxisAlignment.center,
                  children: <Widget>[
                    CircularProgressIndicator(),
                    Padding(
                      padding: EdgeInsets.only(top: 20.0),
                    ),
                    Text('Caricamento'),
                  ],
                ),
              ),
            ],
          )
        ],
      ),
    );
  }
}

Вот мой main.dartфайл, здесь я инициализирую свои вкладки и еще кое-что. Я добавил тудаChangeNotifyer потому что здесь мне нужны данные, может не то место?

import 'package:testapp/localization/app_localization.dart';
import 'package:testapp/pages/home.dart';
import 'package:testapp/pages/maison.dart';
import 'package:testapp/pages/map.dart';
import 'package:testapp/splash.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:provider/provider.dart';
import 'package:testapp/providers/maisons_provider.dart';

void main() {
  LicenseRegistry.addLicense(() async* {
    final license = await rootBundle.loadString('google_fonts/OFL.txt');
    yield LicenseEntryWithLineBreaks(['google_fonts'], license);
  });

  runApp(
    SplashApp(
      key: UniqueKey(),
      onInitializationComplete: () => runMainApp(),
    ),
  );
}

void runMainApp() {
  runApp(
    MyApp(),
  );
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final textTheme = Theme.of(context).textTheme;

    return ChangeNotifierProvider(
      builder: (ctx) => MaisonsProvider(),
      child: MaterialApp(
        debugShowCheckedModeBanner: false,
        title: 'Title',
        theme: ThemeData(
          primaryColor: Colors.black,
          scaffoldBackgroundColor: Color(0xFFDDCDC8),
          visualDensity: VisualDensity.adaptivePlatformDensity,
          textTheme: GoogleFonts.montserratTextTheme(textTheme).copyWith(
            headline1: GoogleFonts.montserrat(
              textStyle: textTheme.headline1,
              fontSize: 30,
              fontWeight: FontWeight.w600,
              color: Colors.black,
            ),
            headline2: GoogleFonts.montserrat(
              textStyle: textTheme.headline1,
              fontSize: 22,
              fontWeight: FontWeight.w500,
              color: Colors.black,
            ),
            bodyText1: GoogleFonts.montserrat(
              textStyle: textTheme.bodyText1,
              color: Colors.black,
              fontWeight: FontWeight.w400,
              fontSize: 19,
            ),
          ),
        ),
        home: BottomBar(),
        routes: {
          MaisonPage.routeName: (ctx) => MaisonPage(),
        },
        supportedLocales: [
          Locale('en', 'US'),
          Locale('it', 'IT'),
        ],
        localizationsDelegates: [
          AppLocalizations.delegate,
          GlobalMaterialLocalizations.delegate,
          GlobalWidgetsLocalizations.delegate,
        ],
      ),
    );
  }
}

class BottomBar extends StatefulWidget {
  @override
  _BottomBarState createState() => _BottomBarState();
}

class _BottomBarState extends State<BottomBar> {
  int _currentIndex = 0;

  final List<Widget> _children = [
    HomePage(),
    MapPage(),
  ];

  void onTabTapped(int index) {
    setState(() {
      _currentIndex = index;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: _children[_currentIndex],
      bottomNavigationBar: BottomNavigationBar(
        backgroundColor: Color(0xFFDDCDC8),
        currentIndex: _currentIndex,
        onTap: onTabTapped,
        items: [
          BottomNavigationBarItem(
            icon: Icon(Icons.list),
            title: Text(
              AppLocalizations.of(context).translate('tab_maisons'),
            ),
          ),
          BottomNavigationBarItem(
            icon: Icon(
              Icons.map,
            ),
            title: Text(
              AppLocalizations.of(context).translate('tab_map'),
            ),
          ),
        ],
      ),
    );
  }
}

Это моя модель от Maison.

import 'package:testapp/models/point.dart';
import 'package:flutter/material.dart';

class Maison {
  final String id;
  final String name;
  final String imageUrl;
  final String price;
  final Point coordinates;

  Maison({
    @required this.id,
    @required this.name,
    @required this.price,
    @required this.imageUrl,
    @required this.coordinates,
  });
}

А вот и провайдер:

import 'package:testapp/models/point.dart';
import 'package:flutter/material.dart';
import '../models/maison.dart';

class MaisonsProvider with ChangeNotifier {
  // Questa lista è privata, non può essere recuperata dall'esterno, serve un getter
  List<Maison> _items = [
    Maison(
      id: '1',
      name: 'Prova 1',
      price: '60€',
      imageUrl:
          'https://images.unsplash.com/photo-1495562569060-2eec283d3391?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=1500&q=80',
      coordinates: Point(
        lat: 45.4640976,
        lng: 9.1897378,
      ),
    ),
    Maison(
      id: '2',
      name: 'Prova 2',
      price: 'Free',
      imageUrl:
          'https://images.unsplash.com/photo-1582559934353-2e47140e7501?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=1788&q=80',
      coordinates: Point(
        lat: 45.4640976,
        lng: 10.1897378,
      ),
    ),
    Maison(
      id: '3',
      name: 'Prova 3',
      price: 'Free',
      imageUrl:
          'https://images.unsplash.com/photo-1553355617-f7342d67a95f?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=1500&q=80',
      coordinates: Point(
        lat: 45.9,
        lng: 10.1897378,
      ),
    ),
    Maison(
      id: '4',
      name: 'Prova 4',
      price: '40€',
      imageUrl:
          'https://images.unsplash.com/photo-1555141816-810dd5692b6a?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=1789&q=80',
      coordinates: Point(
        lat: 46.4640976,
        lng: 9.30,
      ),
    ),
  ];

  // Getter
  // fa in modo che venga ritornata la lista di Maison,
  // non è la lista originale ma una copia.
  List<Maison> get items {
    return [..._items];
  }

  // Aggiunge una nuova Maison alla lista.
  // è importante passare dal provider così che sia lui a informare gli altri widget
  // che c'è stato un cambiamento
  void addMaison(Maison maison) {
    _items.add(maison);

    // Informa tutti i widget che sono interessati che c'è una nuova maison
    // in questo modo i widget possono fare una rebuild e mostrare i dati corretti
    notifyListeners();
  }
}

Я добавил addMaison метод, который должен быть ответственным за добавление нового Дома в мой _item и геттер, чтобы получить его копию.

Я попытался запустить своего провайдера в splash.dart следующим образом:

final maisonsData = Provider.of<MaisonsProvider>(context);
Maison m = Maison(
    id: '1',
    name: 'Prova add',
    imageUrl: 'https://upload.wikimedia.org/wikipedia/commons/1/17/Google-flutter-logo.png',
    coordinates: {
        lat: 23,
        lng: 23,
    }
);
maisonData.addMaison(m);

и я попытался добавить его в свой _initializeAsyncDependenciesно приложения продолжают показывать заставку и не могут продолжить.
Как я могу использовать провайдера в HTTP-запросе?

1 ответ

Решение

Поставщики - действительно хороший инструмент для обновления вашего дерева виджетов без вызова метода setstate, который он работает в части дерева.
класс ChangeNotifier:

в этом классе вы можете обрабатывать любую логику своего приложения, в вашем случае отправляет запрос, управляющий ответом данных.

Конструктор ChangeNotifier provider widget:

здесь вы инициализируете свой класс уведомления об изменениях, он должен быть выше в дереве виджетов, поэтому дочерний элемент, зависящий от кода вашей логики (уведомитель об изменении), получает уведомление, когда выполняется некоторая логика.

Как прислушиваться к изменениям Consumer widget and provider.of<ChangeNotifierClass>(context):

эти виджеты слушают notifyListeners() вызовите внутри вашего класса уведомителя об изменениях, поэтому каждый раз, когда логика запускает весь дочерний виджет, который слушает ваш ChangeNotifier, будет запускаться метод сборки (так что это в основном похоже на вызов setstate).

Итак, для вашего вопроса вам просто нужно реализовать логику вашего запроса, создавая списки из данных json и прочего.... чем запускать notifylisteners для отображения и нового состояния ваших виджетов, которые интересуются этими данными.
Для меня было довольно сложно принять такую ​​логику, но после того, как я сделал это однажды, я ясно увидел, что она хорошо сочетается с флаттером, и для получения дополнительной помощи вы можете проверить архитектуру блочного шаблона.
надеюсь, это помогло вам удачи.

Другие вопросы по тегам