close
İçeriğe geç

Flutter Sayaç

başlangıç

Bu eğitimde, Bloc kütüphanesini kullanarak Flutter ile bir Sayaç uygulaması oluşturacağız.

demo

Yepyeni bir Flutter projesi oluşturarak başlayacağız

Terminal window
flutter create flutter_counter

Ardından pubspec.yaml dosyasının içeriğini şununla değiştirebiliriz

pubspec.yaml
name: flutter_counter
description: A new Flutter project.
version: 1.0.0+1
publish_to: none
environment:
sdk: ^3.12.0
dependencies:
bloc: ^9.0.0
flutter:
sdk: flutter
flutter_bloc: ^9.1.0
dev_dependencies:
bloc_lint: ^0.3.0
bloc_test: ^10.0.0
flutter_test:
sdk: flutter
integration_test:
sdk: flutter
mocktail: ^1.0.0
flutter:
uses-material-design: true

ve sonrasında tüm bağımlılıklarımızı kuralım

Terminal window
flutter pub get
├── lib
│ ├── app.dart
│ ├── counter
│ │ ├── counter.dart
│ │ ├── cubit
│ │ │ └── counter_cubit.dart
│ │ └── view
│ │ ├── counter_page.dart
│ │ ├── counter_view.dart
│ │ └── view.dart
│ ├── counter_observer.dart
│ └── main.dart
├── pubspec.lock
├── pubspec.yaml

Uygulama, özellik odaklı bir dizin yapısı kullanır. Bu proje yapısı, kendi içinde bağımsız özelliklere sahip olarak projeyi ölçeklendirmemizi sağlar. Bu örnekte yalnızca tek bir özelliğimiz olacak (sayacın kendisi), ancak daha karmaşık uygulamalarda yüzlerce farklı özelliğimiz olabilir.

İlk inceleyeceğimiz şey, uygulamadaki tüm durum değişikliklerini gözlemlememize yardımcı olacak bir BlocObserver'ın nasıl oluşturulacağıdır.

lib/counter_observer.dart dosyasını oluşturalım:

lib/counter_observer.dart
import 'package:bloc/bloc.dart';
/// {@template counter_observer}
/// [BlocObserver] for the counter application which
/// observes all state changes.
/// {@endtemplate}
class CounterObserver extends BlocObserver {
/// {@macro counter_observer}
const CounterObserver();
@override
void onChange(BlocBase<dynamic> bloc, Change<dynamic> change) {
super.onChange(bloc, change);
// ignore: avoid_print
print('${bloc.runtimeType} $change');
}
}

Bu durumda, gerçekleşen tüm durum değişikliklerini görmek için yalnızca onChange metodunu geçersiz kılıyoruz.

Sırada, lib/main.dart dosyasının içeriğini şununla değiştirelim:

lib/main.dart
import 'package:bloc/bloc.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_counter/app.dart';
import 'package:flutter_counter/counter_observer.dart';
void main() {
Bloc.observer = const CounterObserver();
runApp(const CounterApp());
}

Az önce oluşturduğumuz CounterObserver'ı başlatıyor ve birazdan inceleyeceğimiz CounterApp widget'ı ile runApp'i çağırıyoruz.

lib/app.dart dosyasını oluşturalım:

CounterApp bir MaterialApp olacak ve home olarak CounterPage'i belirtecektir.

lib/app.dart
import 'package:flutter/material.dart';
import 'package:flutter_counter/counter/counter.dart';
/// {@template counter_app}
/// A [MaterialApp] which sets the `home` to [CounterPage].
/// {@endtemplate}
class CounterApp extends MaterialApp {
/// {@macro counter_app}
const CounterApp({super.key}) : super(home: const CounterPage());
}

Sırada CounterPage'e bakalım!

lib/counter/view/counter_page.dart dosyasını oluşturalım:

CounterPage widget'ı, bir CounterCubit (birazdan inceleyeceğiz) oluşturmaktan ve bunu CounterView'a sağlamaktan sorumludur.

lib/counter/view/counter_page.dart
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_counter/counter/counter.dart';
/// {@template counter_page}
/// A [StatelessWidget] which is responsible for providing a
/// [CounterCubit] instance to the [CounterView].
/// {@endtemplate}
class CounterPage extends StatelessWidget {
/// {@macro counter_page}
const CounterPage({super.key});
@override
Widget build(BuildContext context) {
return BlocProvider(
create: (_) => CounterCubit(),
child: const CounterView(),
);
}
}

lib/counter/cubit/counter_cubit.dart dosyasını oluşturalım:

CounterCubit sınıfı iki metot sunacak:

  • increment: mevcut duruma 1 ekler
  • decrement: mevcut durumdan 1 çıkarır

CounterCubit'in yönettiği durumun türü yalnızca bir int'tir ve başlangıç durumu 0'dır.

lib/counter/cubit/counter_cubit.dart
import 'package:bloc/bloc.dart';
/// {@template counter_cubit}
/// A [Cubit] which manages an [int] as its state.
/// {@endtemplate}
class CounterCubit extends Cubit<int> {
/// {@macro counter_cubit}
CounterCubit() : super(0);
/// Add 1 to the current state.
void increment() => emit(state + 1);
/// Subtract 1 from the current state.
void decrement() => emit(state - 1);
}

Sırada, durumu tüketmekten ve CounterCubit ile etkileşim kurmaktan sorumlu olacak CounterView'a bakalım.

lib/counter/view/counter_view.dart dosyasını oluşturalım:

CounterView, mevcut sayacı görüntülemekten ve sayacı artırmak/azaltmak için iki FloatingActionButton oluşturmaktan sorumludur.

lib/counter/view/counter_view.dart
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_counter/counter/counter.dart';
/// {@template counter_view}
/// A [StatelessWidget] which reacts to the provided
/// [CounterCubit] state and notifies it in response to user input.
/// {@endtemplate}
class CounterView extends StatelessWidget {
/// {@macro counter_view}
const CounterView({super.key});
@override
Widget build(BuildContext context) {
final textTheme = Theme.of(context).textTheme;
return Scaffold(
body: Center(
child: BlocBuilder<CounterCubit, int>(
builder: (context, state) {
return Text('$state', style: textTheme.displayMedium);
},
),
),
floatingActionButton: Column(
mainAxisAlignment: MainAxisAlignment.end,
crossAxisAlignment: CrossAxisAlignment.end,
children: <Widget>[
FloatingActionButton(
key: const Key('counterView_increment_floatingActionButton'),
child: const Icon(Icons.add),
onPressed: () => context.read<CounterCubit>().increment(),
),
const SizedBox(height: 8),
FloatingActionButton(
key: const Key('counterView_decrement_floatingActionButton'),
child: const Icon(Icons.remove),
onPressed: () => context.read<CounterCubit>().decrement(),
),
],
),
);
}
}

CounterCubit durumu her değiştiğinde metni güncellemek için Text widget'ı bir BlocBuilder ile sarmalanmıştır. Ek olarak, en yakın CounterCubit örneğini bulmak için context.read<CounterCubit>() kullanılır.

lib/counter/view/view.dart dosyasını oluşturun:

Counter view'in tüm dışa açık parçalarını dışa aktarmak için view.dart dosyasını ekleyin.

lib/counter/view/view.dart
export 'counter_page.dart';
export 'counter_view.dart';

lib/counter/counter.dart dosyasını oluşturalım:

Counter özelliğinin tüm dışa açık parçalarını dışa aktarmak için counter.dart dosyasını ekleyin.

lib/counter/counter.dart
export 'cubit/counter_cubit.dart';
export 'view/view.dart';

İşte bu kadar! Sunum katmanını iş mantığı katmanından ayırdık. CounterView, bir kullanıcı bir düğmeye bastığında ne olacağını bilmez; sadece CounterCubit'i bilgilendirir. Üstelik CounterCubit de durumla (sayaç değeri) ne olduğundan habersizdir; çağrılan metotlara karşılık olarak yalnızca yeni durumlar yayar.

Uygulamamızı flutter run ile çalıştırabilir ve cihazımızda veya simülatör/emülatörde görüntüleyebiliriz.

Bu örneğin tam kaynak kodu (birim ve widget testleri dahil) buradan ulaşılabilir.