Dart/Flutter da Json Ayrıştırma 💫 🌌 ✨

JSON’u ayrıştırmak, İnternet’ten veri alması gereken uygulamalar için çok yaygın bir görevdir.

Ne kadar JSON verisi işlemeniz gerektiğine bağlı olarak iki seçeneğiniz vardır:

  1. Tüm JSON ayrıştırma kodunu manuel olarak yazın
  2. Kod oluşturma ile süreci otomatikleştirin

Bu kılavuz, aşağıdakiler dahil olmak üzere JSON’un Dart koduna manuel olarak nasıl ayrıştırılacağına odaklanacaktır :

☼ JSON encoding (kodlama) ve decoding (kod çözme)
☼ Type-safe (tip güvenli) model sınıfları tanımlama
☼ Factory cunstructor kullanarak JSON’u Dart koduna ayrıştırma
☼ Nullable/optional (null yapılabilir/isteğe bağlı) değerlerle çalışmak
☼ Data validation (veri doğrulama)
☼ JSON serializing
☼ Complex/nested (karmaşık/iç içe) JSON verilerini ayrıştırma
☼ deep_pick paketinin kullanımı

Bu makalenin sonunda, sağlam JSON ayrıştırma ve doğrulama koduyla model sınıflarının nasıl yazılacağını öğreneceksiniz.

Ve bir sonraki makalede , tüm ayrıştırma kodunu elle yazmak zorunda kalmamak için kod oluşturma araçlarıyla (code generation tools) JSON ayrıştırma hakkında bilgi edineceksiniz.



JSON Kodlama ve Kod Çözme (Encoding and Decoding JSON)

Ağ üzerinden bir JSON yanıtı gönderildiğinde, yükün tamamı bir dize olarak kodlanır .

Ancak Flutter uygulamalarımızın içinde, verileri bir dizeden manuel olarak çıkarmak istemiyoruz:

final json = ' "name": "Pizza da Mario", "cuisine": "Italian", "reviews": ["score": 4.5,"review": "The pizza was amazing!","score": 5.0,"review": "Very friendly staff, excellent service!"]';

Bunun yerine JSON’un kodunu çözerek içeriği okuyabiliyoruz .

JSON verilerini ağ üzerinden göndermek için önce kodlanması (encoded) veya serileştirilmesi (serialized) gerekir . Kodlama , bir veri yapısını (data structure) bir string’e dönüştürme işlemidir . Ters işlem, decoding veya deserialization (seri durumdan çıkarma) olarak adlandırılır . Dize olarak bir JSON yükü aldığınızda, kullanmadan önce kodunu çözmeniz (decode) veya seri durumdan çıkarmanız (deserialize) gerekir.



Dart ile JSON kodunu çözme:dönüştürme (Decoding JSON with dart:convert)

Basit olması için, bu küçük JSON yükünü ele alalım:

//bu, ağdan aldığımız bazı yanıt verilerini temsil eder, örneğin:
// final response = await http.get(uri);
// final jsonData = response.body
final jsonData=" "name": "Pizza da Mario", "cuisine": "Italian" ";

İçindeki anahtarları ve değerleri okumak için önce dart:convert paketini kullanarak kodunu çözmemiz gerekiyor

// 1. import dart:convert
import 'dart:convert';
// bu, ağdan aldığımız bazı yanıt verilerini temsil eder
final jsonData=" "name": "Pizza da Mario", "cuisine": "Italian" ";
// 2. json kodunu çöz
final parsedJson = jsonDecode(jsonData);
// 3. türü ve değeri yazdır
print('$parsedJson.runtimeType : $parsedJson');

Bu kodu çalıştırırsak şu çıktıyı alırız:

_InternalLinkedHashMap<String, dynamic> : name: Pizza da Mario, cuisine: Italian

Pratikte, sonuç türü ile aynıdır Map.

_InternalLinkedHashMap , sırayla Map’i uygulayan LinkedHashMap’in özel bir uygulamasıdır.

Bu nedenle, anahtarlar String ve değerler dynamic türündedir . Bu mantıklıdır çünkü her JSON değeri primitive type (ilkel bir tür => boolean/number/string) veya bir koleksiyon (list veya map) olabilir.

Aslında, jsonDecode(), içinde ne olduğuna bakılmaksızın herhangi bir geçerli JSON yükü üzerinde çalışan genel bir yöntemdir. Tek yaptığı, kodunu çözmek ve dinamik bir değer döndürmek.

Ancak Dart’ta dinamik değerlerle çalışırsak, strong type-safety (güçlü tip güvenliğinin) tüm avantajlarını kaybederiz. Çok daha iyi bir yaklaşım, duruma göre (on a case-by-case basis) yanıt verilerimizi temsil eden bazı özel model sınıfları tanımlamaktır.

Dart statik olarak yazılmış bir dil olduğundan, JSON verilerini gerçek dünya nesnelerini (yemek tarifi, çalışan vb.) temsil eden model sınıflarına dönüştürmek ve tür sisteminden (type system) en iyi şekilde yararlanmak önemlidir.

Öyleyse bunun nasıl yapılacağını görelim.



JSON’u bir Dart modeli sınıfına ayrıştırma

Bu basit JSON verildiğinde:


  "name": "Pizza da Mario",
  "cuisine": "Italian"

Onu temsil edecek bir Restaurantsınıfı yazabiliriz:

class Restaurant 
  Restaurant(required this.name, required this.cuisine);
  final String name;
  final String cuisine;

Sonuç olarak, verileri şöyle okumak yerine:

parsedJson['name']; // dynamic
parsedJson['cuisine']; // dynamic

şöyle okuyabiliriz:

restaurant.name; //geçersiz kılınamaz, değişmez bir string olması garanti edilir
restaurant.cuisine; // geçersiz kılınamaz, değişmez bir stringolması garanti edilir

Bu çok daha temizdir ve derleme zamanı güvenliği ( compile-time safety) elde etmek ve yazım hatalarını ve diğer hatalardan kaçınmak için tür sisteminden yararlanabiliriz.

Ancak, parsedJson’umuzu bir Restaurant nesnesine nasıl dönüştüreceğimizi henüz belirlemedik!



JSON da Dart’a Factory Constructor Ekleme

Bunu halletmek için bir factory constructor tanımlayalım:

factory Restaurant.fromJson(Map<String, dynamic> data) 
  // note the explicit cast to String
  // this is required if robust lint rules are enabled
  final name = data['name'] as String;
  final cuisine = data['cuisine'] as String;
  return Restaurant(name: name, cuisine: cuisine);

Bir factory constructor, sonucu döndürmeden önce bazı işler yapmamıza (değişkenler oluşturma, bazı doğrulamalar gerçekleştirme) izin verdiği için JSON ayrıştırması (parsing) için iyi bir seçimdir . Bu, normal (generative-üretken) constructor’larla mümkün değildir.

Constructor’ı şu şekilde kullanabiliriz:

// type: String
final jsonData=" "name": "Pizza da Mario", "cuisine": "Italian" ";
// type: dynamic (runtime type: _InternalLinkedHashMap<String, dynamic>)
final parsedJson = jsonDecode(jsonData);
// type: Restaurant
final restaurant = Restaurant.fromJson(parsedJson);

Çok daha iyi. Artık kodumuzun geri kalanı Restaurant class’ını kullanabilir ve Dart’ta güçlü tip güvenliğinin (type-safety) tüm avantajlarını elde edebilir.

Bazen , belirli bir key/value çiftine sahip olan veya olmayan bazı JSON’ları ayrıştırmamız gerekir .

Örneğin, bir restoranın ilk ne zaman açıldığını bize bildiren isteğe bağlı bir alanımız olduğunu varsayalım:


  "name": "Ezo Sushi",
  "cuisine": "Japanese",
  "year_opened": 1990

Year_opened alanı isteğe bağlıysa (optional), onu model sınıfımızda boş bir değişkenle (nullable variable) temsil edebiliriz.

İşte Restaurant sınıfı için güncellenmiş bir uygulama:

class Restaurant 
  Restaurant(required this.name, required this.cuisine, this.yearOpened);
  final String name; // non-nullable
  final String cuisine; // non-nullable
  final int? yearOpened; // nullable

  factory Restaurant.fromJson(Map<String, dynamic> data) 
    final name = data['name'] as String; // cast as non-nullable String
    final cuisine = data['cuisine'] as String; // cast as non-nullable String
    final yearOpened = data['year_opened'] as int?; // cast as nullable int
    return Restaurant(name: name, cuisine: cuisine, yearOpened: yearOpened);
  

Genel bir kural olarak, isteğe bağlı JSON değerlerini (optional JSON values) null yapılabilir Dart özelliklerine (nullable Dart properties) eşlemeliyiz . Alternatif olarak, bu örnekte olduğu gibi, null yapılamayan Dart özelliklerini (non-nullable Dart properties) mantıklı bir varsayılan değerle kullanabiliriz:

// note: all the previous properties have been omitted for simplicity
class Restaurant 
  Restaurant(
    // 1. required
    required this.hasIndoorSeating,
  );
  // 2. *non-nullable*
  final bool hasIndoorSeating;

  factory Restaurant.fromJson(Map<String, dynamic> data) 
    // 3. cast as *nullable* bool
    final hasIndoorSeating = data['has_indoor_seating'] as bool?;
    return Restaurant(
      // 4. varsayılan bir dddeğer atamak için ?? operatörünü kullanmak
      hasIndoorSeating: hasIndoorSeating ?? true,
    );
  

Bu durumda, varsayılan bir değer sağlamak için boş birleştirme operatörünü (null-coalescing operator) (??) nasıl kullandığımıza dikkat edin.



Veri Doğrulama

Factory constructor kullanmanın bir yararı, gerekirse bazı ek doğrulamalar yapabilmemizdir.

Örneğin, gerekli bir değer eksikse UnsupportedError veren bir savunma kodu yazabiliriz.

factory Restaurant.fromJson(Map<String, dynamic> data) 
  // casting as a nullable String so we can do an explicit null check
  final name = data['name'] as String?;
  if (name == null) 
    throw UnsupportedError('Invalid data: $data -> "name" is missing');
  
  final cuisine = data['cuisine'] as String?;
  if (cuisine == null) 
    throw UnsupportedError('Invalid data: $data -> "cuisine" is missing');
  
  final yearOpened = data['year_opened'] as int?;
  // Yukarıdaki if ifadeleri sayesinde, burada name ve cuisine değerlerinin  boş olmayacağı garanti edilir.
  return Restaurant(name: name, cuisine: cuisine, yearOpened: yearOpened);

Genel olarak, her bir değer için çalışmak API tüketicisi olarak bizim işimizdir:

türü (String, int, vb.)
isteğe bağlıysa veya değilse (nullable vs non-nullable)
hangi değer aralığına izin verilirse

Bu, JSON ayrıştırma kodumuzu daha sağlam hale getirecektir. Ve tüm doğrulamalar önceden yapıldığından , widget sınıflarımızda geçersiz verilerle uğraşmak zorunda kalmayacağız .



toJson() ile JSON Serileştirme

JSON’u ayrıştırmak yararlıdır, ancak bazen bir model nesnesini JSON’a geri dönüştürmek ve ağ üzerinden göndermek isteriz.

Bunu yapmak için Restaurant sınıfımız için bir toJson() yöntemi tanımlayabiliriz:

// note the return type
Map<String, dynamic> toJson() 
  // return a map literal with all the non-null key-value pairs
  return 
    'name': name,
    'cuisine': cuisine,
    if (yearOpened != null) 'year_opened': yearOpened,
  ;

Ve bunu şöyle kullanabiliriz:

// bir Restoran nesnesi verildi
final restaurant = Restaurant(name: "Patatas Bravas", cuisine: "Spanish");
// convert it to map
final jsonMap = restaurant.toJson();
// onu bir JSON dizgisine kodla
final encodedJson = jsonEncode(jsonMap);
// sonra herhangi bir ağ paketiyle istek gövdesi olarak gönder



İç İçe JSON’ı Ayrıştırma: List of Map

Artık JSON ayrıştırma ve doğrulamanın temellerini anladığımıza göre, ilk örneğimize geri dönelim ve nasıl ayrıştırılacağını görelim:


  "name": "Pizza da Mario",
  "cuisine": "Italian",
  "reviews": [
    
      "score": 4.5,
      "review": "The pizza was amazing!"
    ,
    
      "score": 5.0,
      "review": "Very friendly staff, excellent service!"
    
  ]

Model sınıflarını ve tip güvenliğini (type-safety) sonuna kadar kullanmak istiyoruz, bu yüzden bir Review sınıfı tanımlayalım:

class Review 
  Review(required this.score, this.review);
  // non-nullable - puan alanının her zaman mevcut olduğu varsayılır
  final double score;
  // nullable - inceleme alanının isteğe bağlı olduğunu varsayarsak
  final String? review;

  factory Review.fromJson(Map<String, dynamic> data) 
    final score = data['score'] as double;
    final review = data['review'] as String?;
    return Review(score: score, review: review);
  

  Map<String, dynamic> toJson() 
    return 
      'score': score,
      // burada, null değerleri hesaba katmak için collection-if kullanıyoruz
      if (review != null) 'review': review,
    ;
  

Ardından, bir reviews listesi eklemek için Restoran class’ını güncelleyebiliriz:

class Restaurant 
  Restaurant(
    required this.name,
    required this.cuisine,
    this.yearOpened,
    required this.reviews,
  );
  final String name;
  final String cuisine;
  final int? yearOpened;
  final List<Review> reviews;

Ayrıca factory cunstructor’ını da güncelleyebiliriz:

factory Restaurant.fromJson(Map<String, dynamic> data) 
  final name = data['name'] as String;
  final cuisine = data['cuisine'] as String;
  final yearOpened = data['year_opened'] as int?;
  // reviews eksik olabileceğinden null yapılabilir bir listeye dönüştürün
  final reviewsData = data['reviews'] as List<dynamic>?;
  // reviews eksik değilse
  final reviews = reviewsData != null
      // her review'i  bir Review object'e eşleyin
      ? reviewsData.map((reviewData) => Review.fromJson(reviewData))
        // map() yinelenebilir bir değer döndürür böylece onu listeye çevirebiliriz
        .toList()
      // geri dönüş değeri olarak boş bir liste kullanın
      : <Review>[];
  // tüm argümanları geçen sonucu döndür
  return Restaurant(
    name: name,
    cuisine: cuisine,
    yearOpened: yearOpened,
    reviews: reviews,
  );

• reviews eksik olabilir, bu nedenle nullable bir Listeye yayınlıyoruz.
• listedeki değerlerin herhangi bir türü olabilir, bu nedenle List kullanıyoruz.
• Review.fromJson() kullanarak her dinamik değeri bir Review nesnesine dönüştürmek için .map() operatörünü kullanırız.
• reviews eksikse, yedek olarak boş bir liste ([]) kullanırız.

Bu özel uygulama, neyin boş olup olmayacağı, hangi yedek değerlerin kullanılacağı vb. hakkında bazı varsayımlarda bulunur. Kullanım durumunuz için en uygun ayrıştırma kodunu yazmanız gerekir.



İç İçe (Nested) Modelleri Serileştirme

Son adım olarak, bir Restoranı tekrar Map’e dönüştürmek için toJson() yöntemini burada bulabilirsiniz:

Map<String, dynamic> toJson() 
  return 
    'name': name,
    'cuisine': cuisine,
    if (yearOpened != null) 'year_opened': yearOpened,
    'reviews': reviews.map((review) => review.toJson()).toList(),
  ;

Tüm iç içe değerleri de serileştirmemiz gerektiğinden (yalnızca Restaurant sınıfının kendisini değil) List öğesini nasıl List>’e dönüştürdüğümüze dikkat edin.

Yukarıdaki kod ile bir Restaurant nesnesi oluşturup onu tekrar kodlanıp yazdırılabilen veya ağ üzerinden gönderilebilen bir map’e dönüştürebiliriz:

final restaurant = Restaurant(
  name: 'Pizza da Mario',
  cuisine: 'Italian',
  reviews: [
    Review(score: 4.5, review: 'The pizza was amazing!'),
    Review(score: 5.0, review: 'Very friendly staff, excellent service!'),
  ],
);
final encoded = jsonEncode(restaurant.toJson());
print(encoded);
// output: "name":"Pizza da Mario","cuisine":"Italian","reviews":["score":4.5,"review":"The pizza was amazing!","score":5.0,"review":"Very friendly staff, excellent service!"]



Derin Değerler (Deep Values) Seçmek

Tüm bir JSON belgesini type-safe model sınıflarına ayrıştırmak çok yaygın bir kullanım durumudur.

Ancak bazen derinden iç içe (deeply nested) olabilecek bazı belirli değerleri okumak isteriz.

Örnek JSON’umuzu bir kez daha ele alalım:


  "name": "Pizza da Mario",
  "cuisine": "Italian",
  "reviews": [
    
      "score": 4.5,
      "review": "The pizza was amazing!" 
    ,
    
      "score": 5.0,
      "review": "Very friendly staff, excellent service!"
    
  ]

reviews listesindeki ilk score değerini almak isteseydik şöyle yazardık:

final decodedJson = jsonDecode(jsonData); // dynamic
final score = decodedJson['reviews'][0]['score'] as double;

Bu geçerli Dart kodudur çünkü decodedJson değişkeni dinamiktir ve onunla birlikte indis operatörünü kullanabiliriz ([]).

Ancak yukarıdaki kod ne null safe ne de type safe ve ayrıştırılan değeri açıkça istediğimiz türe (double) çevirmemiz gerekiyor.

Bunu nasıl iyileştirebiliriz?



deep_pick Paketi

Deep_pick paketi, tür açısından type-safe API ile JSON parsing’i basitleştirir.

import 'dart:convert';
import 'package:deep_pick/deep_pick.dart';

final decodedJson = jsonDecode(jsonData); // dynamic
final score = pick(decodedJson, 'reviews', 0, 'score').asDoubleOrThrow();

deep_pick, ilkel türleri (primitive types), listeleri, mapleri, DateTime nesnelerini ve daha fazlasını ayrıştırmak için kullanabileceğimiz çeşitli esnek API’ler sunar.



toString() yöntemi ekleme

Model sınıflarıyla çalışırken, konsola kolayca yazdırılabilmeleri için bir toString() yöntemi sağlamak çok yararlıdır.

Zaten bir toJson() yöntemimiz olduğundan, onu şu şekilde kullanabiliriz:

@override
String toString() => toJson().toString();

Sonuç olarak, restoranımızı doğrudan şu şekilde yazdırabiliriz:

print(restaurant);
// output: name: Pizza da Mario, cuisine: Italian, reviews: [score: 4.5, review: The pizza was amazing!, score: 5.0, review: Very friendly staff, excellent service!]



Performans Hakkında Not

Küçük JSON belgelerini ayrıştırdığınızda, uygulamanızın yanıt vermeye devam etmesi ve performans sorunları yaşamaması muhtemeldir.

Ancak çok büyük JSON belgelerinin ayrıştırılması, arka planda en iyi şekilde ayrı bir Dart isolate üzerinde yapılan pahalı hesaplamalara neden olabilir . Resmi belgelerin bu konuda iyi bir kılavuzu var:
JSON’u arka planda ayrıştırın



Çözüm

JSON serileştirme çok sıradan bir iştir. Ancak uygulamalarımızın doğru çalışmasını istiyorsak, bunu doğru yapmamız ve ayrıntılara dikkat etmemiz çok önemlidir:

• JSON verilerini seri hale getirmek için 'dart:convert' öğesinden jsonEncode() ve jsonDecode() kullanın

• Uygulamanızdaki tüm alana özgü (domain-specific) JSON nesneleri için fromJson() ve toJson() ile model sınıfları oluşturun

• Ayrıştırma kodunu daha sağlam hale getirmek için fromJson() içine explicit casts, validation ve boş denetimler(null checks) ekleyin

• İç içe (nested) JSON verileri (list of maps) için fromJson() ve toJson() yöntemlerini uygulayın

• JSON’u tür açısından güvenli(type-safe) bir şekilde ayrıştırmak (parse) için deep_pick paketini kullanmayı düşünün

Farklı model sınıfınız varsa veya her sınıfın birçok özelliği varsa, tüm ayrıştırma kodunu elle yazmak zaman alıcı ve hataya açık hale gelir.

Bu gibi durumlarda kod oluşturma(code generation) çok daha iyi bir seçenektir.

resource


Source link

How to Get Glassmorphism Effect in Flutter

What is Glassmorphism?

Flutter Glassmorphism is a term used to describe UI design that emphasizes light or dark objects, placed on top of colorful backgrounds. A background blur is placed on the objects which allows the background to shine through – giving it the impression of frosted glass

You can wrap your widget with Back Drop Filter and add an image filter but, this process is a bit complicated so, we will see an easy way all you have to do is add a line like this to your package’s pubspec.yaml.

dependencies:
  glassmorphism: ^3.0.0

Once installed you will get access to a widget called GlassmorphicContainer, it has various properties

width: used to control the width of the container.

height: used to control the height of the container.

borderRadius: used to change the border radius.

linearGradient: used to provide background gradient with different colors.
child: which will hold all our widget that will have this effect.

we can add gradient like this for example

LinearGradient(colors: [

                        Colors.white.withOpacity(0.2),

                        Colors.white.withOpacity(0.05)

                      ], begin: Alignment.topLeft, end: Alignment.bottomRight),

this will have the most common white forsted glass effect.

Flutter Glassmorphism

Flutter Glassmorphism

Complete code for Flutter Login Page – FrostedGlass Background Flutter

Find Flutter Login Page example


Source link

Using Stacked Architecture in Flutter App

The concept of Architecture is one of the most diverse topics in the world of programming today. A lot of solutions have been developed, all aimed at fixing one flaw or the other. App Architecture is one of the topics where personal choice has been advised to come in. If one meets your needs efficiently and enables you to release good, quality code, then go for it.

This article aims to introduce you to Stacked Architecture, an architecture that offers clean, efficient solutions to architecture your next Flutter app. From dependency Injection to the right out of the box services to layered structures that follow the principles of Clean Architecture and a whole lot. To get started on development with Flutter, check out this article from the Flutter team.

Stacked Architecture, developed by Dane Mackier from FilledStacks, is an MVVM Architecture solution that offers a lot of components for building a highly scalable, testable, maintainable, and usable Flutter application. From

  • State Management,
  • Dependency Injection (Dependency Inversion),
  • Navigation Abstraction,
  • Services (Out-of-the-box),

And a whole lot more. All of these are offered by the Stacked Architecture.



State Management

Handling State in Flutter application using Stacked couldn’t be much better. It provides widgets and constructors which control how the state is being passed and managed in the application. Let’s dive into it using a sample app.

The first thing is to create a new Flutter project,

     flutter create intro_to_stacked

This command generates all the files and folders, which lays the foundation of the flutter app.
Next is to add stacked as a dependency in the pubspec.yaml file

    stacked: ^2.2.7

Next is to set up the basic folder structure for the sample app. Go to the lib folder and create a new folder titled home. Inside this, create two files named home_view.dart and home_viewmodel.dart. The homeViewModel file would be responsible for managing the state of the homeView, which is shown to the user. Clean Architecture, which says the view should contain zero logic, is strictly adhered to; the logic resides in the ViewModel.

The view is bound to the ViewModel using the ViewModelBuilder widget, which Stacked offers. This widget takes in 2 parameters which are

  • builder, which builds the UI that has its state in the ViewModel
  • viewModelBuilder, which is the function that would return the ViewModel for this widget.
    import 'package:flutter/material.dart';
    import 'package:stacked/stacked.dart';

    class HomeView extends StatelessWidget 
     const HomeView(Key? key) : super(key: key);
     @override
     Widget build(BuildContext context) 
      return ViewModelBuilder<HomeViewModel>.reactive(
       viewModelBuilder: HomeViewModel(),
       builder: (context, model, child) 
        return Scaffold();
       ,
      );
     
    

The ViewModelBuilder offers two constructors that deal with how the state is managed and subsequently update the UI for the application. They are the .reactive() and the .nonReactive() constructors. The .reactive() rebuilds the UI every time notifyListeners is called in the ViewModel. However, the .nonReactive() constructor rebuilds the UI once, after which it doesn’t rebuild on subsequent calls in the ViewModel.

With that, we’ve set up the view file, moving to the ViewModel, we create a class that extends the BaseViewModel class that stacked provides. The BaseViewModel class provides functionalities used for maintaining the state in the application. In the ViewModel, we have a String that says Stacked is cool (definitely true). This String is the state currently offered by the ViewModel and which would need to be displayed on the screen.

    import 'package:stacked/stacked.dart';

    class HomeViewModel extends BaseViewModel 
     final String _declaration = 'Stacked is soo cool';
     String get myDeclaration => _declaration;

    

Back to the view, to use this state in our builder, we use the model offered by the builder connecting the state in the ViewModel to the UI screen which uses it.

    Scaffold(
         body: Center(
          child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
           children: [
            const Text('Stacked Introduction'),
            Text(model.myDeclaration),
           ],
          ),
         ),
        );

On updating the state in the application, let’s have a function that updates the text and calls notifyListeners after. We would link this function to the onPressed of a TextButton.

    //In View(home_view.dart)
     TextButton(
      onPressed: () => model.updateDeclaration(),
      child: const Text('Update Text'),
     )

    //In ViewModel (home_viewmodel.dart)
     void updateCounter() 
      _declaration = 'I say Stacked is sooo cool';
      notifyListeners();
     

The notifyListeners call informs the view to rebuild as there has been a state change in the ViewModel.

For a deep dive into the state management solution offered, check out this article provided by the FilledStacks team.



How about Navigation and Dependency Injection?

Stacked provides a direct out the box to navigation without context. Using the NavigationService it gives, we can smoothly declare our navigation in our ViewModel and use them in the view. This NavigationService allows for a clean UI without the interference of logic or complex routing codes. With the aid of the build runner and stacked generator, we can auto-generate routes and smoothly perform navigation.

Let’s get to it.

The first is to add build_runner and stacked_generator to the dev_dependencies section in your pubspec.yaml file. Also, add the stacked_services to the dependency section.

    dependencies:
     stacked: ^2.2.7
     stacked_services: ^0.8.15

    dev_dependencies:
     build_runner: ^2.1.4
     stacked_generator: ^0.5.5

Run the flutter pub get command to get the files locally for use.

Next is to create the new view that we would be routing to. Create a folder inside lib and name it profile. Inside this folder, create two files, the profile_view.dart and profile_viewmodel.dart.

In profile_viewmodel.dart, create a class named ProfileViewModel which extends BaseViewModel. In the ViewModel, we declare a String that the app would display on the view.

    import 'package:stacked/stacked.dart';

    class ProfileViewModel extends BaseViewModel 
     String _pageName="This is the Profile Page";
     String get pageName => _pageName;
    

In profile_view.dart, create the stateless widget which returns the ViewModelBuilder and bind it to the ProfileViewModel. This view would contain a similar setup to the HomeView; it would display a Profile View text.

    import 'package:flutter/material.dart';
    import 'package:intro_to_stacked/ui/views/profile/profile_viewmodel.dart';
    import 'package:stacked/stacked.dart';
    class ProfileView extends StatelessWidget 
     const ProfileView(Key? key) : super(key: key);
     @override
     Widget build(BuildContext context) 
      return ViewModelBuilder<ProfileViewModel>.reactive(
       viewModelBuilder: () => ProfileViewModel(),
       builder: (context, viewModel, child) 
        return Scaffold(
         body: Center(
          child: Column(
           mainAxisAlignment: MainAxisAlignment.center,
           children: [
            Text(viewModel.pageName),
           ],
          ),
         ),
        );
       ,
      );
     
    

The next thing is to create a folder in lib named app. Inside this folder, create a new file titled app.dart. In this file would all the code needed for routing along with the dependencies.

Create a class named AppSetup and annotate it with the StackedApp; this class houses the annotation; it does nothing else asides that. The main focus is the annotation as it takes in two parameters, routes, and dependencies.

    @StackedApp()
    class AppSetup 
     /** This class has no puporse besides housing the annotation that generates the required functionality **/
    

Stacked offers three different types of routes which determines the mode of transition

  • MaterialRoute
  • CupertinoRoute
  • CustomRoute

Using the MaterialRoute, we register the Views present in the app that would use the NavigationService.

In addition to the routes, we would declare the dependencies we would be using in the app, enabling Stacked to create the locator file that would handle the injection of the declared types. We would register the NavigationService as a LazySingleton, which means it won’t be initialized until used for the first time in the app.

    import '../ui/views/home/home_view.dart';
    import '../ui/views/profile/profile_view.dart';
    import 'package:stacked/stacked_annotations.dart';
    @StackedApp(
     routes: [
      MaterialRoute(page: HomeView, initial: true),
      MaterialRoute(page: ProfileView)
     ],
     dependencies: [
      LazySingleton(classType: NavigationService),
     ],
    )
    class AppSetUp 

As the HomeView is the first page we contact, we set the initial parameter to true.

After setting this up, run the flutter command to auto-generate the needed files for routing.

    flutter pub run build_runner build --delete-conflicting-outputs

This command would generate the files needed for navigation and dependency injection setup.

The last step in this process is to convert the main top-level function of the app into a Future and await the setupLocator function in the app.locator.dart file generated by the command used earlier.

    Future main() async 
     WidgetsFlutterBinding.ensureInitialized();
     await setupLocator();
     runApp(MyApp());
    



How do we use it?

To make use of the navigation in the HomeView, we create a function in the HomeViewModel that would perform the navigation. First, we declare the locator which would give us access to the Navigation Service we registered earlier.

    import 'package:intro_to_stacked/app/app.router.dart';
    import '../../../app/app.locator.dart';
    import 'package:stacked/stacked.dart';
    import 'package:stacked_services/stacked_services.dart';

    class HomeViewModel extends BaseViewModel 
     String _declaration = 'Stacked is soo cool';
     String get myDeclaration => _declaration;
     final _navigationService = locator<NavigationService>();

     void navigateToProfileView() 
      _navigationService.navigateTo(Routes.profileView);
     
    

The next thing is to create a button in the view and pass the function to its onPressed parameter.

     TextButton(
       onPressed: () => model.navigateToProfileView(),
       child: const Text('Go To Profile View'),
     )

Save the files and run the app; you would see the button there, click the button and see the seamless transition that we achieve using the NavigationService Stacked provides.



Services

Stacked offers several services which can be set up and used within the app. These services include

  • NavigationService
  • BottomSheetService
  • DialogService
  • SnackbarService

These services can be set up and customized to fit various use cases, and similar to the NavigationService, they can be registered and used within the app on the fly. For further information, check out the package on pub.dev and also the articles on FilledStacks.



Conclusion

Stacked Architecture is a super pack ready to be discharged and used to build clean, scalable applications on the fly. This article has explained some of the concepts and components of Stacked Architecture, and with it, we have created a sample app that uses these components. Check out FilledStack for further information on the various features that make up the Stacked Architecture.

You can find the code for the sample app here.


Source link

My Experience Using Flutter Web?

On March 3, 2021, with the launch of Flutter 2.0 Flutter team officially launched the stable version of Flutter Web. Every flutter fan’s and critic’s eye was on this launch, including me. I did not jump on the flutter web train immediately, but I waited two months to start my experimentation.

And I would say anything else but use the word disappointment. In my eyes, it didn’t deliver on what I expected. They could have done better, but I made myself believe that the web was not the goal of Flutter. It had always been mobile.

What discouraged me from using it?

It was the high loading time when starting the app on chrome for the first time. Hot Reload worked very well, but Hot Restart was not perfect. And when I searched the web for the solution, I was unable to find a satisfactory result.

There is one thing which I would like to mention. I had no prior knowledge of web development before trying flutter web. That may have caused me such chaos.

And in the end, after contacting many lead developers, I tried using loader animation. The way to implement that is to add that in the index.html file. I overcame that problem, but still, I believe that there is no proper method to optimize your flutter web app for search engines. That in itself is a considerable drawback as of right now.

So do I recommend Flutter Web?

As of right now, NO. But having a look at the pace of updates by the Flutter Team, I see a bright future for Flutter Web and the Flutter framework itself.

And I positively stand by my point that Flutter is the next big thing. Or Flutter is the beast but still in development.

You can follow me :

LinkedIn -> AasharWahla

YouTube -> Aashar Wahla

Instagram ->Aashar.Wahla




Source link

StringBuffer in Dart – DEV Community

Imagine you are building the next Billion Dollar app 💵 that has a lot of innovative features which are new to the tech world. But your app crashes a lot! Further investigation proved that the code you wrote for the app is not optimized.
Uff ! Thats a bummer 😐. What would you do?

Well, you call me 📲 !
Nahh, just open this tutorial.✅

Most of the apps out there incorporate String Concatenation in some or the other way. It could be to join the First Name and Last Name of a person, or get the list of cities the user has travelled this year, and the list goes on.

Most of the developers including me, follow one approach for string concatenation, ie, using the ‘+’ operator.
Here is a basic example to illustrate what i meant:

String? firstName = "Aswin";
String? lastName = "Gopinathan";

String? fullName = firstName + " " + lastName;

This is the most typical approach, and it’s easier too. But this approach comes with a price.

And the price is memory !!

Let’s see another example where we concatenate strings from a List:

List<String> cities = ["Bangalore","Mumbai","Delhi","Chennai"];

String finalCities = "";

for(String city in cities) 
  finalCities += (city+' ');


print(finalCities);

The output will be:

Bangalore Mumbai Delhi Chennai 

You might have read that Strings in dart are immutable. But, we just updated finalCities multiple times. How is it even possible?

Well, the answer to this magic is that every time we concatenate a new string, the compiler allocates a new memory for the resultant string, and the garbage collector is called to de-allocate the previous memory location.

So literally the garbage collector is called for every item you concatenate, and for each of those iterations a new memory location is assigned !!

Finding it hard to believe? Its okay, i understand.

Let’s do one thing. Write this line of code inside the for loop from the previous code snippet:

print(identityHashCode(finalCities));

This line of code prints the hashcode of the memory address of the variable finalCities.

What do you see? The output should be like this:

260230835
327507326
116916136
283166850

The values may differ for you.

So, it is pretty clear from this that the variable finalCities uses different memory location in every iteration.

But dont worry, if i bummed you out with this, i will fix it too !!

Enter : StringBuffer 🥁🥁

Me : “Welcome StringBuffer to this article”
StringBuffer : “Ahoy ! Let me introduce myself. My name is StringBuffer. I take care of the mess that my ancestor String creates. You know he is pretty old-school.
Me : “I know but he is still one of the best. So without any further delays, let me tell everyone how you work.”

So, as the official dart docs says :
“A class for concatenating strings efficiently. Allows for the incremental building of a string using write() methods. The strings are concatenated to a single string only when toString is called.”

Let me break that down for you.

StringBuffer is a class that lets you concatenate string efficiently. Duh! you just read that above.
Well, how it works is you use the inbuilt method write() to concatenate strings instead of the + operator. But what happens with StringBuffer is that, it stores the string in a buffer and not in a memory location. It uses the memory only when the toString() method is called to create a String object.

Tada! Now you don’t have to worry about the memory being allocated every time you concatenate.

See, StringBuffer is a superhero 😎

Well, thats all for the theory part 😉
But don’t go yet, let’s get our hands dirty. Shall we? 😋

So, lets open a new dart file and start off with the main():

void main() 
  // Enter code here

Let’s create a StringBuffer object first. We will be using the same List as before:

StringBuffer sb = StringBuffer();

List<String> cities = ["Bangalore","Mumbai","Delhi","Chennai"];

Now, rewrite the for loop as follows:

for(String city in cities) 
  sb.write(city+" ");
  // print(identityHashCode(sb));

We use the write() to append new strings to the end. You can uncomment the print() statement to confirm no new memory location is created for every concatenation.

Well, now print the result:

String cityList = sb.toString();
print(cityList);

It is at this point a memory location is created to print the concatenated string.

Wow! Thats really great. Now you have reduced one of the many causes of memory leaks in your app 👏👏.
Now, you are one step closer to releasing that Billion-dollar app.

So, with that i am ending this article. But this is not the actual end. I am still exploring Flutter and Dart and as i learn anything new, i will be back with more articles.

So till then, Bye bye !!! 👋


Source link

Flutter & Dart Tips – Week In Review #16

Hello Reader,

I am getting very close to the goal of this series of sharing 100+ FlutterDart tips. Let’s get into the 16th post of this.

15

1- Transform is a widget that applies a transformation before painting its child.


...

  Container(
  color: Colors.black,
  child: Transform(
    alignment: Alignment.topRight,
    transform: Matrix4.skewY(0.3)..rotateZ(-math.pi / 12.0),
    child: Container(
      padding: const EdgeInsets.all(8.0),
      color: const Color(0xFFE8581C),
      child: const Text('Apartment for rent!'),
    ),
  ),
)

...

Try it on DartPad here

RadioListTile

2- VerticalDivider widget is a one device pixel thick vertical line, with padding on either side.


...


Row(
  mainAxisSize: MainAxisSize.min,
  children: <Widget>[
    Container(
      child: Text('Item1')
    ),
    VerticalDivider(),
    Container(
      child: Text('Item2'),
    ),
    VerticalDivider(),
    Container(
      child: Text('Item3'),
    ),
  ],
)

...

Try it on DartPad here

RangeSlider

3- TimePicker widget shows a dialog containing a material design time picker.


...


Future<TimeOfDay> selectedTime = showTimePicker(
  initialTime: TimeOfDay.now(),
  context: context,
);

...

Try it on DartPad here

RefreshIndicator

4- Stepper widget displays progress through a sequence of steps.


...


    Stepper(
          steps: [
            Step(
              title: new Text('Login Info'),
              content: Column(
                children: <Widget>[
                  TextFormField(
                    decoration: InputDecoration(labelText: 'Email Address'),
                  ),
                  TextFormField(
                    decoration: InputDecoration(labelText: 'Password'),
                  ),
                ],
              ),
              isActive: true,
              state: StepState.editing
            ),
          ],
        )

...

Try it on DartPad here

Switch

5- ActionChip widget used to create compact elements called chips, which trigger an action when the user presses on it.


...


  ActionChip(
    label: Text('Delete'),
    onPressed: () 
      print('Processing to delete item');
    
  )

...

Try it on DartPad here

Tooltip

6- ExpansionPanel widget is a panel with a header and a body and can be either expanded or collapsed. The body of the panel is only visible when it is expanded.


...


        ExpansionPanel(
          headerBuilder: (context, isExpanded) 
            return ListTile(
              title: Text(
                'Click To Expand',
                style: TextStyle(color: Colors.black),
              ),
            );
          ,
          body: ListTile(
            title: Text('Description text',
                style: TextStyle(color: Colors.black)),
          ),
          isExpanded: _expanded,
          canTapOnHeader: true,
        ),

...

Try it on DartPad here

SelectableText

See you next week. 👋🏻

Follow me on Twitter for more tips about #coding, #learning, #technology…etc.

Check my Apps on Google Play & Apple Store

Cover image XPS on Unsplash




Source link

Flutter Learning Roadmap 2021 – DEV Community

Flutter Learning Roadmap 2021. Learning will be beneficial only if you are doing it in effective way. If you are trying to learning a stuff and you are doing it in a very bad way then you will not be able to get the most out of it. Find this post we are going to discuss the best effective way of learning flutter.

What is Flutter
Flutter is a brand new Framework developed by Google team. It is developed in Dart language. Flutter is the one of the best way to quickly make apps for different platforms with one codebase.

Learn Dart Basics
The first thing that you have to do before starting learning flutter is that you have to learn Dart programming language. Because Flutter uses Dart as its programming language so that you should have intermediate understanding of Dart syntax. You do not need to be expert in that you just need to know its syntax.

To read complete post and access free Flutter Resources visit original post here


Source link

Flutter App life cycle – DEV Community

Basically when we are writing a code for any Native applications, We will look for a life cycle events to handle some specific scenarios. Its like handling Thanos gauntlet snap to blip 😇😇

Flutter comes with life cycle events to handle app life cycle for android & ios.

Let’s see the code.

To consume the life cycle event, we need to have Stateful widget with WidgetsBindingObserver mixin.

class _HomePageState 
extends State<HomePage> 
with WidgetsBindingObserver {

The mixin WidgetsBindingObserver provides an override didChangeAppLifecycleState where we will notified for certain app life cycle state changes.

  @override
  void didChangeAppLifecycleState(AppLifecycleState state) 
    switch (state) 
      case AppLifecycleState.resumed:
        print('app resumed');
        break;
      case AppLifecycleState.paused:
        print('app paused');
        break;
      case AppLifecycleState.inactive:
        print('app inactive');
        break;
      case AppLifecycleState.detached:
      default:
        print('app detached');
        break;
    
  

Finally to get work all these stuff, we need to inform or observe the app life cycle changes using

@override
  void initState() 
    super.initState();
    WidgetsBinding.instance!.addObserver(this);
  

Ofcourse dont forget to remove observer on dispose

@override
  void dispose() 
    super.dispose();
    WidgetsBinding.instance!.removeObserver(this);
  

That’s it, we can now handle the app as per the app lifecycle.

Happy Fluttering 😇😇


Source link