フラッターアーキテクチャ


ハローリーダー!フラッタは、特にモバイルフィールドでは、長年にわたって普及している.それはほとんど簡単に学習し、大幅に開発の速度を高めることができます.フラッターの初心者には、どうやって自分のコードをきれいにするのですか?どのように、私は州を管理しますか?どうやってコードを構成するのですか?
記事では、あなたのコードをきれいにする方法を示しています.もちろん、柔軟な構造で、あなたによって変更されたり編集されたりすることができます.これは、懸念の分離を考慮します(もちろん、大きな問題です).

我々は何を作成するつもりです


物事を非常に簡単にするには、我々は天気データを取得し、画面上のテキストとしていくつかの表示をOpenWaveTherapAPIを使用してアプリケーションを作成する予定です.最終的なアプリケーションは以下のとおりです

プロジェクト設定


このプロジェクトは、私たちのlibディレクトリに合計6個のフォルダを持つ予定です
  • データ
  • ミックス
  • モデル
  • プロバイダ
  • スクリーン
  • Utils
  • 州の管理のために、我々はHTTP呼び出しのためにプロバイダープラグインとDIOでプロバイダーパターンを利用するつもりです.これを使用するには、Pubspecの依存関係を追加する必要があります.YAMLフォルダ
    プロバイダhttps://pub.dev/packages/provider ]
    diohttps://pub.dev/packages/dio ]

    プロセス


    当社のアプリケーションの流れは、それぞれの特定のタスクを実行する別のクラスに分かれています.
    私はちょうどコードを示して、クラスが何をするかについて説明します、そして、これの終わりに、私は詳細に全体のプロセスを説明します.

    データクラス


    データフォルダでは、我々は2つのクラスweather_data and weather_repo

    The weather_data class is responsible for fetching the weather data from the api, and returning the data to the repo class.
    The getWeather method takes in a dio parameter that will be parse in by the view home_screen.
    When the data has been gotten successfully from the api the weather class feeds the status of the response and the actual data to the Operation class and returns an instance of this class to the Weather Repo


    気象データ


    
    import 'package:dio/dio.dart';
    import 'package:weather_art/models/country_response_model.dart';
    import 'package:weather_art/utils/operation.dart';
    
    class WeatherData{
      Future<Operation> getWeather(Dio dio) async{
    
        try{
          var response = await dio.get(
              'http://api.openweathermap.org/data/2.5/weather?q=lagos&appid={api_key}',
    
          ).timeout(Duration(minutes: 2), onTimeout: () async{
            return Response(
                data: {"message": "Connection Timed out. Please try again"},
                statusCode: 408);
          }).catchError((error) {
            return Response(
                data: {"message": "Error occurred while connecting to server"},
                statusCode: 508);
          });
    
    
          if(response.statusCode == 508 || response.statusCode == 408){
            return Operation(response.statusCode, response.data);
          }else{
    
            WeatherResponse data = WeatherResponse.fromJson(response.data);
    
            return Operation(response.statusCode, data);
          }
    
        }catch(err){
           //catch err
          }
        }
      }
    
    }
    
    final countryData = WeatherData();
    
    The weather_repo クラスはデータクラスを呼び出し、データを返すのを待つものです.

    天気予報


    import 'package:dio/dio.dart';
    import 'package:weather_art/data/weather_data.dart';
    import 'package:weather_art/utils/operation.dart';
    
    class _WeatherRepo{
       getWeatherData(Dio dio, OperationCompleted countryDataCompleted){
          countryData.getWeather(dio).then((data) => countryDataCompleted(data));
       }
    }
    
    _WeatherRepo countryRepo = _WeatherRepo();
    

    mixinクラス


    mixinフォルダでは、1つのクラスを作成し、それを呼び出しますhome_helper . このクラスは、プロバイダによって、取得したデータに応じてビューの状態を変更します.これは、状態を変更するためのコードがビュー自体から分離され、ビュー内で行われるロジックを減らすことを確認します
    import 'package:dio/dio.dart';
    import 'package:flutter/material.dart';
    import 'package:provider/provider.dart';
    import 'package:weather_art/data/weather_repo.dart';
    import 'package:weather_art/models/country_response_model.dart';
    import 'package:weather_art/providers/home_provider.dart';
    import 'package:weather_art/utils/operation.dart';
    
    mixin HomeHelper{
      BuildContext _authContext;
    
      doGetWeather(Dio dio, BuildContext context){
        _authContext = context;
        Provider.of<HomeProvider>(_authContext, listen: false).updateIsLoading(true);
        weatherRepo.getWeatherData(dio, _weatherDataCompleted);
      }
    
      _weatherDataCompleted(Operation operation){
    
        if(operation.code == 408 || operation.code == 508){
          //handle time out
          Provider.of<HomeProvider>(_authContext, listen: false).updateIsLoading(false);
          print('connection timed out');
        }else{
          WeatherResponse weatherResponse = operation.result;
          Provider.of<HomeProvider>(_authContext, listen: false).updateWeather(weatherResponse);
    
          Provider.of<HomeProvider>(_authContext, listen: false).updateIsLoading(false);
    
        }
      }
    }
    

    モデルクラス


    このフォルダで我々の天気モデルを作成します
    // To parse this JSON data, do
    //
    //     final weatherResponse = weatherResponseFromJson(jsonString);
    
    import 'dart:convert';
    
    WeatherResponse weatherResponseFromJson(String str) => WeatherResponse.fromJson(json.decode(str));
    
    String weatherResponseToJson(WeatherResponse data) => json.encode(data.toJson());
    
    class WeatherResponse {
      WeatherResponse({
        this.weather,
        this.main,
    
      });
    
      List<Weather> weather;
      Main main;
    
    
      factory WeatherResponse.fromJson(Map<String, dynamic> json) => WeatherResponse(
        coord: Coord.fromJson(json["coord"]),
        weather: List<Weather>.from(json["weather"].map((x) => Weather.fromJson(x))),
        main: Main.fromJson(json["main"]),
    
      );
    
      Map<String, dynamic> toJson() => {
        "coord": coord.toJson(),
        "weather": List<dynamic>.from(weather.map((x) => x.toJson())),
        "main": main.toJson(),
    
      };
    }
    
    class Main {
      Main({
        this.temp,
    
      });
    
      double temp;
    
    
      factory Main.fromJson(Map<String, dynamic> json) => Main(
        temp: json["temp"].toDouble(),
    
      );
    
      Map<String, dynamic> toJson() => {
        "temp": temp,
      };
    }
    
    class Weather {
      Weather({
        this.main,
        this.description,
      });
      String main;
      String description;
    
      factory Weather.fromJson(Map<String, dynamic> json) => Weather(
    
        main: json["main"],
        description: json["description"],
    
      );
    
      Map<String, dynamic> toJson() => {
        "main": main,
        "description": description,
    
      };
    }
    
    

    プロバイダーフォルダ


    ここで2つのファイルを作成しますHomeProvider and AppProvider

    ホームプロバイダ


    import 'package:flutter/foundation.dart';
    import 'package:weather_art/models/country_response_model.dart';
    
    class HomeProvider extends ChangeNotifier{
      bool isLoading = false;
      List<WeatherResponseModel> weatherList = [];
      WeatherResponseModel weatherResponse;
    
      void updateIsLoading(bool isLoadingGotten){
        isLoading = isLoadingGotten;
        notifyListeners();
      }
    
      void updateWeather(WeatherResponseModel weatherResponseGotten){
        weatherResponse = weatherResponseGotten;
      }
    }
    

    アプリプロバイダ


    import 'package:dio/dio.dart';
    
    class AppProvider{
      Dio dio = Dio();
    
    }
    

    スクリーンフォルダ


    これは私たちのビューがどこにあるか、ここでファイルを作成し、それを呼び出しますhome_screen
    import 'package:flutter/material.dart';
    import 'package:provider/provider.dart';
    import 'package:weather_art/mixin/home_helper.dart';
    import 'package:weather_art/providers/app_provider.dart';
    import 'package:weather_art/providers/home_provider.dart';
    
    class HomeScreen extends StatefulWidget {
      @override
      _HomeScreenState createState() => _HomeScreenState();
    }
    
    class _HomeScreenState extends State<HomeScreen> with HomeHelper{
    
      @override
      void initState() {
        // TODO: implement initState
        super.initState();
    
        WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
          doGetWeather(
              Provider.of<AppProvider>(context, listen: false).dio,
              context
          );
        });
    
      }
      @override
      Widget build(BuildContext context) {
        return Consumer(
            builder: (BuildContext context, HomeProvider homeProvider, Widget child){
              return Scaffold(
                body: Container(
                  height: MediaQuery.of(context).size.height,
                  width: MediaQuery.of(context).size.width,
                  child: decideLayout(homeProvider),
                ),
              );
            }
        );
      }
    
      Widget decideLayout(HomeProvider homeProvider){
        if(homeProvider.isLoading){
          return Center(
            child: CircularProgressIndicator(),
          );
        }else if(homeProvider.isLoading == false && homeProvider.weatherResponse == null){
          return Center(
            child: Text(
              'Null',
              style: TextStyle(
                  fontSize: 14
              ),
            ),
          );
        }else{
          return Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              Text(
                'Weather in Lagos',
                style: TextStyle(
                    fontSize: 18,
                  fontWeight: FontWeight.bold
                ),
              ),
              Text(
                'Looks Like ${homeProvider.weatherResponse.weather[0].main}',
                style: TextStyle(
                    fontSize: 14
                ),
              ),
    
              Text(
                'Description ${homeProvider.weatherResponse.weather[0].description}',
                style: TextStyle(
                    fontSize: 14
                ),
              ),
    
              Text(
                'Temp ${homeProvider.weatherResponse.main.temp.toString()}',
                style: TextStyle(
                    fontSize: 14
                ),
              ),
            ],
          );
        }
      }
    }
    
    

    Utilsフォルダ


    このフォルダにはクラス操作が含まれます.あなたはちょうどあなたのコードでこれをコピーして、これを通り過ぎることができます.クラスは2つのparamsを受け入れます.レスポンスコードと応答データ.このクラスは、操作が完了したかどうかをチェックするために使用されるtypedefまたはコールバックです.
    import 'package:flutter/material.dart';
    
    typedef OperationCompleted(Operation operation);
    
    class Operation {
      final dynamic _result;
      final int code;
      Operation(this.code, this._result);
    
      bool get succeeded => code >= 200 && code <= 226;
      dynamic get result => _result;
    }
    
    

    流れ


    私たちは、すべてのコードとクラスは、このパターンに関与して見てきました、それぞれのいくつかの説明で、今私は一緒にすべてを結合させ、アプリケーションとのユーザーの相互作用に従うことによって、1つの画像を描画し、各接続で何が起こるかを伝える.
    ユーザーは彼が何をすべきかを探している彼の電話をクリックしています.そして、彼は「何が今日のようになるかについて天気です」と言います、そして、彼は我々のアプリを開けるために進みます.ユーザーは私達のhome_screen , 我々home_screen と呼ばれるmixinを使用しますHomeHelper また、HomeProvider それ自身の状態を管理する.
    The isLoadingHomeProvider ユーザーが何かがロードされていることを示す循環的な進展を見るように、クラスはfalseです.これが起こっている間initState 呼び出しdoGetWeather に必要なパラメータを指定します.The doGetWeather ミックスからのHomeHelper . The doGetWeather 呼び出しweatherRepo.getWeatherData_WeatherRepo クラス.これはgetWeatherWeatherData クラスFuture したがって、このメソッドは、何かをgetWeather . getWeather はAPIからデータを取得し、Operation . Operation ステータスコードとAPIからのデータである2つのパラメータを受け入れます.操作がgetWeather , その後、データが後方に渡されます.
    我々は、Repoクラスがまだ若干のデータを予想すると思い出すべきです.then repoクラスを呼び出し、データをcountryDataCompleted , これは順番に_weatherDataCompleted インHomeHelper . ここでは、UIを変更し、ホームプロバイダーで作成したメソッドを使用してデータを更新します.

    結論


    このアーキテクチャを用いることにより、UIを論理から非常によく分離することに成功した.すべてのコードを読みやすく編集する分離されます.

    ソースコード


    [ https://github.com/Marcusjnr/weather ]