folder Tahribat.com Forumları
linefolder Mobil Programlama
linefolder Flutter 2 - Sadece Mobil Uygulama Olmaktan Çıkıyor



Flutter 2 - Sadece Mobil Uygulama Olmaktan Çıkıyor

  1. KısayolKısayol reportŞikayet pmÖzel Mesaj
    YeniHarman
    YeniHarman's avatar
    Kayıt Tarihi: 17/Haziran/2012
    Erkek

    Hım, sanırım fw içinde bu işi sağlayan mekanizma var ama konfor sağlamıyor anladığım.

    Kısaca şöyle diyebilir miyiz?

    Takibini yapmak istediğim tüm verileri singleton jenerik bir koleksiyon içinde sarmalıyorum. Ayrıca değişiklik taleplerini de doğrudan buraya yapıyorum. Değişiklikleri de listenerlara duyuruyorum. 

    Peki solid prensipleri vs. hiç olmuyor mu? Performansa nasıl yansıyor?


    Olaylara karışmayın!
  2. KısayolKısayol reportŞikayet pmÖzel Mesaj
    MhmdAlmz
    MhmdAlmz's avatar
    Kayıt Tarihi: 09/Ağustos/2015
    Erkek

    @YeniHarman her soruna cevap gelecek abi. Bu arada dart ffi ile c bile kodlayabiliyorsun. JITCompiler hallediyor. Direkt donanıma da hitap edebiliyorsun Flutter ile. Anlatacağım 3 4 gündür mobilim :/


    Andolsun kuşluk vaktine ve dindiği zaman o geceye ki, Rabbin sana veda etmedi ve darılmadı! Ve kesinlikle senin için sonu önünden (ahiret dünyadan) daha hayırlıdır. ileride Rabbin sana verecek de hoşnut olacaksın! O, seni bir yetim iken barındırmadı mı? Seni, yol bilmez iken (doğru) yola koymadı mı? Seni bir yoksul iken zengin etmedi mi? Öyle ise, sakın yetime kahretme (onu horlama)! El açıp isteyeni de azarlama! Fakat Rabbinin nimetini anlat da anlat!
  3. KısayolKısayol reportŞikayet pmÖzel Mesaj
    makets
    makets's avatar
    Kayıt Tarihi: 17/Ocak/2010
    Erkek
    YeniHarman bunu yazdı

    Hım, sanırım fw içinde bu işi sağlayan mekanizma var ama konfor sağlamıyor anladığım.

    Kısaca şöyle diyebilir miyiz?

    Takibini yapmak istediğim tüm verileri singleton jenerik bir koleksiyon içinde sarmalıyorum. Ayrıca değişiklik taleplerini de doğrudan buraya yapıyorum. Değişiklikleri de listenerlara duyuruyorum. 

    Peki solid prensipleri vs. hiç olmuyor mu? Performansa nasıl yansıyor?

    Solid prensiplerini uygulayabilirsin, state güncellemesinden tüm sayfayı değil sadece istediğin widgeti render edebilirsin. test driven development, MVVM tercih edebilirsin. MhmdAlmz'ın bahsettiği dartffi i unutmuşum. Flutter 2.0 ile gündemde. Ne kadar efektif, kullandıkça forumda tecrübelerimi paylaşırım. Ben MVVM ve state yönetimi için uzun süredir stacked paketini kullanıyorum. Memnunum. Bu konuda farklı çözümler var. Bakalım MhmdAlmz hangi paket(ler)i tercih ediyor (:

  4. KısayolKısayol reportŞikayet pmÖzel Mesaj
    MhmdAlmz
    MhmdAlmz's avatar
    Kayıt Tarihi: 09/Ağustos/2015
    Erkek

    Merhaba herkese. Bildiğiniz üzere yaklaşık 3 senedir Flutter yazıyorum aktif olarak. İş hayatımda 1 sene öncesine kadar React-Native yazıyordum. Flutter için istihdam yoktu malesef. Gerçi şu an ki işime başladığımda da RN yazıyordum proje yöneticisini kafalayıp tüm şirketi Flutter'a çevirdik :D Neyse..

     

    State Management Nedir?

    State Management belli bir widget'i yada bir class'ı istediğiniz anda güncelleme işine yarar. Şöyle ki State Management olmadan bir şekilde (Singleton Pattern, static değişken vb.) yine de global de değişkenler yada yapılar tanımlayabilirsiniz. Child to parent widget ilişkilerinde çok zorlanıldığından dolayı ve sadece istenilen ekran bölümünü güncellediğimizden dolayı State Management'a ihtiyaç duyarız. Bunun için bir çok kütüphane duyabilirsiniz örnek olarak; Redux, MobX, GetX, Provider vs. vs. Aslında bu State Managementlar şu işe yarıyor. kısaca bir sınıf yazarsak.

    class TestWidget extends StatefulWidget {
      @override
      _TestWidgetState createState() => _TestWidgetState();
    }
    
    class _TestWidgetState extends State {
      @override
      void initState() {
        ObserverClass().listen(() {
          this.setState(() {});
        });
        super.initState();
      }
    
      @override
      Widget build(BuildContext context) {
        return Text("test");
      }
    }
    

    Burada belirtilen ObserverClass sadece dinleme işini görür ve herhangi bir method ile listen methodu çalışır sizin belirttiğiniz widget tekrar render olur hepsi bu. (Kaba taslak arada bazı kontroller var bir önceki state ile şu an ki aynı mı vs diye). Bu bir sınıf olduğundan da her yerde erişebiliyorsunuz.

    Dart FFI

    Dart ffi ile OpenCV projesi geliştirdik. Yani JAVA yada Objective-C kullanmadık direkt Dart FFI üzerinde C++ kodlarını çalıştırarak daha performanslı uygulama geliştirdik. Şu an da çatır çatır çalışıyor. Bir adam da burda yapmış 2 dk google yazdım detaylı incelemedim. https://github.com/westracer/flutter_native_opencv  yani direkt olarak popüler kütüphaneleri bu şekilde kullanabiliyoruz. Sadece C++ değil Python vs de yazabiliyorsunuz.

     

    Ben ne kullanıyorum ?

    Ben sadece RxDart kullanarak observer yapımı kurdum ve onun üzerine geliştirmeler yapıyorum. İstediğim yerden istediğim zaman erişebiliyorum. Bloc mantığında ama tam olarak bloc da değil bloc üzerine kendim bir repository yazarak store, listener, request/response datalarımı alıyorum. Her şey modeller üzerine gerçekleşiyor. Elim alıştı şu an şirketteki herkes bu mimari üzerine ilerliyor. Şu an CI/CD süreçleri için boiler plate ve popüler firebase kütüphanelerini de tek bir json dosyasından kontrolünü gerçekleştirecek proje geliştiriyorum. Bitince sizinle de paylaşırım.

     

    Repository

    import 'dart:async';
    
    import 'package:flutter/material.dart';
    import 'package:rxdart/rxdart.dart';
    import 'package:uuid/uuid.dart';
    
    abstract class BlocRepository<ResultObject, RequestObject> {
      RequestObject _requestObject;
      bool _isBlocHandling = false;
      ResultObject _store;
      final _fetcher = PublishSubject();
      Uuid _uuidGenerator = new Uuid();
      String _lastRequestUniqueId = "";
      Function _listener;
    
      PublishSubject get fetcher => this._fetcher;
    
      Observable get stream => _fetcher.stream;
    
      ResultObject get store => this._store;
    
      // String get lastRequestUniqueId => this._lastRequestUniqueId;
    
      bool get isBlocHandling => this._isBlocHandling;
    
      RequestObject get requestObject => this._requestObject;
    
      void setStore(store) => this._store = store;
    
      void clearStore() {
        this._store = null;
        this.fetcher.sink.add(null);
      }
    
      Future prevProcess() async {
        this._lastRequestUniqueId = this._uuidGenerator.v4();
        await process(this._lastRequestUniqueId);
        this._isBlocHandling = false;
      }
    
      Future process(String lastRequestUniqueId);
    
      Future call({RequestObject requestObject, bool sinkNullObject}) {
        this._isBlocHandling = true;
        this._requestObject = requestObject ?? null;
        if (sinkNullObject != null && sinkNullObject) this.fetcher.sink.add(null);
        this.prevProcess();
      }
    
      void fetcherSink(ResultObject resultObject, {bool forceSink, @required lastRequestUniqueId}) {
        if ((forceSink != null && forceSink) || lastRequestUniqueId == this._lastRequestUniqueId) {
          this._store = resultObject;
          this.fetcher.sink.add(resultObject);
          if (this._listener != null) this._listener(resultObject);
        } else {
    //      print("this request is old request" + ResultObject.toString());
        }
      }
    
      void setListener(Function listener) => this._listener = listener;
    
      dispose() {
        this.fetcher.close();
      }
    }

     

    Örnek Kullanım

    class TestRequest {
      final int count;
    
      TestRequest({@required this.count});
    }
    
    class TestResponse {
      List list;
    
      TestResponse({@required this.list});
    }
    
    // Result store model
    // Request username , password
    class TestBloc extends BlocRepository<TestResponse, TestRequest> {
      @override
      Future process(String lastRequestUniqueId) async {
        TestRequest request = this.requestObject;
        this.fetcherSink(
          this.store,
          lastRequestUniqueId: null,
          forceSink: true,
        );
        print("istek atıldı ${request.count}");
        await Future.delayed(Duration(seconds: request.count));
        print("Request Obj ${request.count}");
        List responseList = [];
        for (int i = 0; i < request.count; i++) {
          responseList.add(i * i);
        }
        this.fetcherSink(
          TestResponse(list: responseList),
          lastRequestUniqueId: null,
          forceSink: true,
        );
      }
    }
    
    TestBloc testBloc = TestBloc();
    
    class TestScreen extends StatefulWidget {
      @override
      _TestScreenState createState() => _TestScreenState();
    }
    
    class _TestScreenState extends State {
      @override
      void initState() {
        testBloc.setListener((TestResponse response) {
          if (response == null) return;
          print("Listener -> ${response.list}");
        });
        super.initState();
      }
    
      void _onPress() async {
        testBloc.call(requestObject: TestRequest(count: 2));
        testBloc.call(requestObject: TestRequest(count: 3));
        testBloc.call(requestObject: TestRequest(count: 4));
        testBloc.call(requestObject: TestRequest(count: 1));
    
        // testBloc.store;
      }
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          body: StreamBuilder(
            stream: testBloc.stream,
            builder: (_, __) {
              Widget body;
              if (testBloc.isBlocHandling) body = SpinKitRing(color: Colors.red);
              if (testBloc.store == null) body = Text("BOŞ");
              body = ListView.builder(
                itemCount: testBloc.store.list.length,
                itemBuilder: (_, index) {
                  return Text("i'ni Karesi: ${testBloc.store.list[index]}");
                },
              );
              return Container(
                alignment: Alignment.center,
                padding: EdgeInsets.all(20),
                child: Column(
                  mainAxisAlignment: MainAxisAlignment.center,
                  children: [
                    Expanded(child: body, flex: 1),
                    MaterialButton(
                      onPressed: _onPress,
                      minWidth: 300,
                      child: testBloc.isBlocHandling ? SpinKitCircle(color: Colors.white) : Text("Test Button"),
                      color: Colors.red,
                    ),
                  ],
                ),
              );
            },
          ),
        );
      }
    }

    Kütüphaneler

    flutter_spinkit

    - uuid

    - rxdart

     

    Örnekte Modeli Bloc'u vs hepsini tek dosyada tutabilirsiniz yada dosyalarınızı yapılayabilirsiniz. Bir kıyak daha yapayım. HttpClient sınıfı paylaşayım. Kullanırsınız belki.

    HttpClient Response Repository

    class HttpResponseRepository {
      bool hasError;
      int statusCode;
      String errorCode;
      String errorMessage;
      T response;
    
      HttpResponseRepository({
        this.hasError = false,
        this.statusCode = 200,
        this.errorCode = "",
        this.errorMessage = "",
        @required this.response,
      });
    }
    
    HttpClient
    class HttpClient {
      static HttpResponseRepository _errorModel() => HttpResponseRepository(
            response: null,
            hasError: true,
            errorMessage: "Üzgünüz, bir sorun oluştu.",
            statusCode: -1,
            errorCode: "",
          );
    
      static Map<String, String> generateHeaders({@required bool withAuth, int page = 1, int pageSize = 1000}) {
        Map<String, String> headers = {
          "Accept-Language": "tr-TR",
          "User-Zone": "Europe/Istanbul",
          "User-Platform": "mobile",
          "X-Page-Size": "${pageSize}",
          "X-Page": "${page}",
        };
        if (withAuth) {
          headers = {...headers, "Authorization": "Bearer ${userInformationBloc.store.token}"};
        }
        return headers;
      }
    
      /*
      Http Error hata kodları 400
       */
    
      static Future _get({
        Map<String, String> headers,
        String url,
        CancelToken cancelToken,
      }) async {
        return await Dio().get(
          url,
          options: Options(headers: headers),
          cancelToken: cancelToken,
        );
      }
    
      static Future _post({
        Map<String, String> headers,
        String url,
        dynamic data,
        CancelToken cancelToken,
      }) async {
        return await Dio().post(
          url,
          options: Options(headers: headers),
          cancelToken: cancelToken,
          data: data,
        );
      }
    
      static Future _delete({
        Map<String, String> headers,
        String url,
        dynamic data,
        CancelToken cancelToken,
      }) async {
        return await Dio().delete(
          url,
          options: Options(headers: headers),
          cancelToken: cancelToken,
          data: data,
        );
      }
    
      static Future _patch({
        Map<String, String> headers,
        String url,
        dynamic data,
        CancelToken cancelToken,
      }) async {
        return await Dio().patch(
          url,
          options: Options(headers: headers),
          cancelToken: cancelToken,
          data: data,
        );
      }
    
      static _printError(apiUrl, headers, data, DioError dioError) {
        if (Application.appMode == AppMode.production) return;
        debugPrint("""
    ====================ERROR REQUEST====================
    URL: ${apiUrl}
    HEADERS: ${headers}
    REQUEST-DATA: ${jsonEncode(data)}
    RESPONSE-ERROR: ${dioError}
    RESPONSE-DATA: ${dioError?.response}
    =======================================================
          """, wrapWidth: 1024);
      }
    
      static _printSuccess(apiUrl, headers, data, Response dioResponse) {
        if (Application.appMode == AppMode.production) return;
        debugPrint("""
    ====================SUCCESS REQUEST====================
    URL: ${apiUrl}
    HEADERS: ${headers}
    REQUEST-DATA: ${jsonEncode(data)}
    RESPONSE: ${dioResponse?.data}
    =======================================================
          """, wrapWidth: 1024);
      }
    
      static Future _refreshToken() async {
        HttpResponseRepository responseRepository = await HttpClient.call(
          type: HttpCallType.get,
          apiUrl: HttpClientApiUrl.refreshToken,
          withAuth: false,
          data: {"AccessToken": "${userInformationBloc.store.token}", "RefreshToken": userInformationBloc.store.refreshToken},
          dynamicResponse: true,
        );
        if (responseRepository.hasError) {
          AppDialogs.showError("Oturumunuz Sonlandırılmıştır Lütfen Tekrar Giriş Yapınız.", onPress: () {
            Navigator.of(Application.context).pushAndRemoveUntil(
              MaterialPageRoute(builder: (_) => LoginScreen()),
              (route) => false,
            );
          });
          return false;
        }
        await AppSharedPreferences.setUserInformationToken(
          DateTime.now().millisecondsSinceEpoch + (responseRepository.response["expiresIn"] * 1000),
          responseRepository.response["token"],
          responseRepository.response["refreshToken"],
        );
        await userInformationBloc.refreshUserInformation();
        return true;
      }
    
      static Future call({
        @required HttpCallType type,
        T Function(dynamic) fromJSON,
        String apiUrlString,
        HttpClientApiUrl apiUrl = null,
        dynamic data,
        int page,
        int pageSize,
        bool dynamicResponse = false,
        bool withAuth = true,
        CancelToken cancelToken,
      }) async {
        if (fromJSON == null && dynamicResponse == false) throw ("Please set fromJSON response");
        if (type == null) throw ("Please set HttpCallType");
        Map<String, String> headers = generateHeaders(withAuth: withAuth, page: page, pageSize: pageSize);
        Response dioResponse;
        String url = apiUrlString ?? apiUrl.uri;
        String queryString = "";
        try {
          if (type == HttpCallType.get) {
            if (data != null) {
              queryString = _getQueryString(data);
              queryString = queryString.substring(1, queryString.length);
            }
            url = "${url}?${queryString}";
            dioResponse = await _get(
              url: url,
              cancelToken: cancelToken,
              headers: headers,
            );
          } else if (type == HttpCallType.patch) {
            dioResponse = await _patch(
              data: data,
              url: url,
              cancelToken: cancelToken,
              headers: headers,
            );
          } else if (type == HttpCallType.post) {
            dioResponse = await _post(
              data: data,
              url: url,
              cancelToken: cancelToken,
              headers: headers,
            );
          } else if (type == HttpCallType.delete) {
            dioResponse = await _delete(
              data: data,
              url: url,
              cancelToken: cancelToken,
              headers: headers,
            );
          }
          _printSuccess(url, headers, data, dioResponse);
          return HttpResponseRepository(
            response: dynamicResponse ? dioResponse.data : fromJSON(dioResponse.data),
          );
        } on DioError catch (dioError) {
          _printError(url, headers, data, dioError);
          if (dioError.response.statusCode == 401) {
            bool reCall = await _refreshToken();
            if (reCall) {
              return await call(
                type: type,
                fromJSON: fromJSON,
                apiUrlString: apiUrlString,
                apiUrl: apiUrl,
                data: data,
                dynamicResponse: dynamicResponse,
                withAuth: withAuth,
                cancelToken: cancelToken,
              );
            }
            return null;
          }
          if (dioError.response.statusCode == 500) {
            return _errorModel();
          }
          try {
            if (dioError.response.data["error_message"] == null && dioError.response.data["statusMessage"] == null) return _errorModel();
            return HttpResponseRepository(
              response: null,
              hasError: true,
              errorMessage: dioError.response.data["error_message"] ?? dioError.response.data["statusMessage"],
              statusCode: dioError.response.data["status_code"] ?? dioError.response.data["statusCode"],
              errorCode: dioError.response.data["error_code"] ?? dioError.response.data["errorCode"],
            );
          } catch (err) {
            return _errorModel();
          }
        } catch (err) {
          // print(err);
          return _errorModel();
        }
      }
    
      static String _getQueryString(Map params, {String prefix: '&', bool inRecursion: false}) {
        String query = '';
    
        params.forEach((key, value) {
          if (inRecursion) {
            key = '[$key]';
          }
          if (value is List) {
            value.forEach((element) {
              query += '$prefix$key=${Uri.encodeComponent("${element}")}';
            });
          } else if (value is String || value is int || value is double || value is bool) {
            query += '$prefix$key=${Uri.encodeComponent(value)}';
          } else if (value is List || value is Map) {
            if (value is List) value = value.asMap();
            value.forEach((k, v) {
              query += _getQueryString({k: v}, prefix: '$prefix$key', inRecursion: true);
            });
          }
        });
        return query;
      }
    }
    

    Örnek Kullanım

    class AddCardBloc extends BlocRepository<AddCardResponseBM, AddCardRequestBM> {
      @override
      Future process(String lastRequestUniqueId) async {
        HttpResponseRepository<AddCardResponseBM> responseRepository = await HttpClient.call<AddCardResponseBM>(
          type: HttpCallType.post,
          apiUrl: HttpClientApiUrl.cards,
          data: {
            "cardAlias": this.requestObject.alias,
            "cardHolderName": this.requestObject.name,
            "cardNumber": this.requestObject.number,
            "expireMonth": this.requestObject.date.split("/")[0],
            "expireYear": this.requestObject.date.split("/")[1],
          },
          withAuth: true,
          fromJSON: (json) => AddCardResponseBM.fromJSON(json),
        );
        if (responseRepository.hasError) {
          AppDialogs.showError(responseRepository.errorMessage);
          return this.fetcherSink(null, lastRequestUniqueId: lastRequestUniqueId);
        }
        this.fetcherSink(responseRepository.response, lastRequestUniqueId: null, forceSink: true);
        getCardsBloc.call();
        Navigator.of(Application.context).pop();
      }
    }
    
    AddCardBloc addCardBloc = AddCardBloc();
    

    Sağlıcaklar :) Ek sorularınızı burdan sorarsanız sevinirim herkes faydalansın. Yakın zamanda (Takribi 3 ay) bu gibi özel kütüphanelerimi public bir repo da paylaşacağım github dan. Native den Flutter'a data gönderirken Channel kullanmamak, Native bir ekran tasarımını dart ile çağırmak. Örneğin Java ile bir kod buldunuz adam native yazmış ekran/component native açılıyor bunu dart da nasıl göstereceğiniz gibi gibi....

     

     

    MhmdAlmz tarafından 08/Mar/21 10:20 tarihinde düzenlenmiştir

    Andolsun kuşluk vaktine ve dindiği zaman o geceye ki, Rabbin sana veda etmedi ve darılmadı! Ve kesinlikle senin için sonu önünden (ahiret dünyadan) daha hayırlıdır. ileride Rabbin sana verecek de hoşnut olacaksın! O, seni bir yetim iken barındırmadı mı? Seni, yol bilmez iken (doğru) yola koymadı mı? Seni bir yoksul iken zengin etmedi mi? Öyle ise, sakın yetime kahretme (onu horlama)! El açıp isteyeni de azarlama! Fakat Rabbinin nimetini anlat da anlat!
  5. KısayolKısayol reportŞikayet pmÖzel Mesaj
    makets
    makets's avatar
    Kayıt Tarihi: 17/Ocak/2010
    Erkek

    hocam ellerine sağlık, sorularım var :)

    aynı anda future ve stream olan 2 ayrı state dinleyen bir widget için bu çözümü önerir misin

    view ve model dışında bir classtan state'i takip edip istediğimiz widgetları render edebilir miyiz

    Bunların testlerini yazdıysan onları da paylaşma imkanın var mı

  6. KısayolKısayol reportŞikayet pmÖzel Mesaj
    MhmdAlmz
    MhmdAlmz's avatar
    Kayıt Tarihi: 09/Ağustos/2015
    Erkek
    makets bunu yazdı

    hocam ellerine sağlık, sorularım var :)

    aynı anda future ve stream olan 2 ayrı state dinleyen bir widget için bu çözümü önerir misin

    view ve model dışında bir classtan state'i takip edip istediğimiz widgetları render edebilir miyiz

    Bunların testlerini yazdıysan onları da paylaşma imkanın var mı

    Evet hocam yapabiliyorsun. Repository de görürsün mesela örnekteki gibi testBloc.store dediğinde her yerden erişim sağlıyorsun. StreamBuilder response  gerek yok istersen stream builder verdiği scaffold dan alabilirsin.

    Bunun dışında aynı anda future ve stream olan 2 ayrı state de dinlersin hocam. testBloc.listener bu işe yarıyor. Mesela bende bir yapı var header de sepet var ve bir çok ekranda da sepet'i etkileyen işlemler var. Sepet ayrı bir widget ekranalrın altındaki işlemler ayrı bir widget ama ikisi arasında da köprü var. 

     

    Unit Test yazmadım hocam. 


    Andolsun kuşluk vaktine ve dindiği zaman o geceye ki, Rabbin sana veda etmedi ve darılmadı! Ve kesinlikle senin için sonu önünden (ahiret dünyadan) daha hayırlıdır. ileride Rabbin sana verecek de hoşnut olacaksın! O, seni bir yetim iken barındırmadı mı? Seni, yol bilmez iken (doğru) yola koymadı mı? Seni bir yoksul iken zengin etmedi mi? Öyle ise, sakın yetime kahretme (onu horlama)! El açıp isteyeni de azarlama! Fakat Rabbinin nimetini anlat da anlat!
  7. KısayolKısayol reportŞikayet pmÖzel Mesaj
    Cihan
    Cihan's avatar
    Kayıt Tarihi: 26/Mart/2014
    Erkek

    beyler flutteri öğrenecek bir acemi için, başlangıç olarak ne önerirsiniz acaba?


    wordpress & sözlük
  8. KısayolKısayol reportŞikayet pmÖzel Mesaj
    motka
    motka's avatar
    Kayıt Tarihi: 28/Ekim/2012
    Erkek

    @MhmdAlmz Hocam ellerine sağlık. Kodlara bir göz attım. Bazı yapıları bilmediğimden tam oturmadı bende. Sanırım biraz Bloc yapısı hakkında bilgi edinmek gerekiyor.

  9. KısayolKısayol reportŞikayet pmÖzel Mesaj
    makets
    makets's avatar
    Kayıt Tarihi: 17/Ocak/2010
    Erkek
    MhmdAlmz bunu yazdı
    makets bunu yazdı

    hocam ellerine sağlık, sorularım var :)

    aynı anda future ve stream olan 2 ayrı state dinleyen bir widget için bu çözümü önerir misin

    view ve model dışında bir classtan state'i takip edip istediğimiz widgetları render edebilir miyiz

    Bunların testlerini yazdıysan onları da paylaşma imkanın var mı

    Evet hocam yapabiliyorsun. Repository de görürsün mesela örnekteki gibi testBloc.store dediğinde her yerden erişim sağlıyorsun. StreamBuilder response  gerek yok istersen stream builder verdiği scaffold dan alabilirsin.

    Bunun dışında aynı anda future ve stream olan 2 ayrı state de dinlersin hocam. testBloc.listener bu işe yarıyor. Mesela bende bir yapı var header de sepet var ve bir çok ekranda da sepet'i etkileyen işlemler var. Sepet ayrı bir widget ekranalrın altındaki işlemler ayrı bir widget ama ikisi arasında da köprü var. 

     

    Unit Test yazmadım hocam. 

    eyvallah hocam teşekkür ederim paylaşımın için.

  10. KısayolKısayol reportŞikayet pmÖzel Mesaj
    motka
    motka's avatar
    Kayıt Tarihi: 28/Ekim/2012
    Erkek

    https://www.youtube.com/channel/UCdUaAKTLJrPZFStzEJnpQAg/playlists

    Bu kanalda Flutter hakkinda farkli konulara deginilmis meragi olanlar bir goz atabilir.

Toplam Hit: 8239 Toplam Mesaj: 41
flutter flutter engage flutter