반응형

get_it 패턴은 Flutter 애플리케이션에서 의존성 주입(Dependency Injection)을 간단하고 효율적으로 관리하기 위해 사용되는 패턴입니다. get_it 패키지는 서비스 로케이터(Service Locator) 패턴을 구현하여, 애플리케이션의 상태와 서비스를 전역적으로 접근할 수 있게 합니다.

 

주요 개념

 

1. 서비스 로케이터(Service Locator):

의존성 주입의 한 형태로, 애플리케이션의 다양한 부분에서 공통된 서비스를 쉽게 가져올 수 있게 합니다.

서비스 로케이터를 통해 인스턴스를 전역적으로 관리하고 재사용할 수 있습니다.

2. 의존성 주입(Dependency Injection):

클래스 간의 의존성을 주입하여 코드의 재사용성과 테스트 용이성을 높입니다.

객체를 직접 생성하는 대신, 필요한 의존성을 외부에서 주입합니다.

 

주요 기능

 

등록 및 해제:

서비스를 등록하고 필요 시 해제할 수 있습니다.

싱글톤 및 팩토리:

싱글톤 인스턴스와 팩토리 인스턴스를 지원합니다.

전역 접근:

애플리케이션 어디서나 서비스에 접근할 수 있습니다.

 

예제

 

간단한 예제를 통해 get_it 패턴을 사용하는 방법을 살펴보겠습니다.

 

1. 패키지 추가

 

pubspec.yaml 파일에 get_it 패키지를 추가합니다.

get_it

2. 서비스 클래스 정의

class CounterService {
  int _count = 0;

  int get count => _count;

  void increment() {
    _count++;
  }

  void decrement() {
    _count--;
  }
}

3. 서비스 등록

 

애플리케이션의 초기화 단계에서 서비스를 등록합니다.

import 'package:flutter/material.dart';
import 'package:get_it/get_it.dart';
import 'counter_service.dart';

void setupLocator() {
  GetIt.I.registerLazySingleton(() => CounterService());
}

void main() {
  setupLocator();
  runApp(MyApp());
}

4. 서비스 사용

 

애플리케이션의 다양한 부분에서 서비스를 사용합니다.

 

import 'package:flutter/material.dart';
import 'package:get_it/get_it.dart';
import 'counter_service.dart';

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: CounterPage(),
    );
  }
}

class CounterPage extends StatelessWidget {
  final CounterService _counterService = GetIt.I<CounterService>();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Get It Example'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text(
              'Counter: ${_counterService.count}',
              style: TextStyle(fontSize: 24),
            ),
            SizedBox(height: 20),
            Row(
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                FloatingActionButton(
                  onPressed: _counterService.increment,
                  child: Icon(Icons.add),
                ),
                SizedBox(width: 20),
                FloatingActionButton(
                  onPressed: _counterService.decrement,
                  child: Icon(Icons.remove),
                ),
              ],
            ),
          ],
        ),
      ),
    );
  }
}

 

 

1. 서비스 등록:

GetIt.I.registerLazySingleton(() => CounterService());를 사용하여 서비스를 등록합니다. 이는 싱글톤 인스턴스를 생성합니다.

registerFactory를 사용하면 요청할 때마다 새로운 인스턴스를 생성합니다.

2. 서비스 사용:

GetIt.I<CounterService>()를 사용하여 등록된 서비스를 가져옵니다.

이를 통해 애플리케이션의 어디서나 동일한 서비스 인스턴스를 사용할 수 있습니다.

 

 

다른 예시 (실습)

파일구성

locator 패키지

- locator.dart

service 패키지
- album_service.dart 

view 패키지
- album_getit_view.dart
메인 메소드 
- locator 메소드 호출

 

album_service.dart

import 'dart:convert';

import '../model/album.dart';
import 'package:http/http.dart' as http;

abstract class AlbumService {
  Future<List<Album>> fetchAlbums();
}


class AlbumServiceImplementation implements AlbumService {
  @override
  Future<List<Album>> fetchAlbums() async {
    final reponse = await http.get(
        Uri.parse("https://jsonplaceholder.typicode.com/albums")
    );
    final List<Album> result = jsonDecode(reponse.body)
        .map<Album>((json) => Album.fromJson(json))
        .toList();

    return result;
  }
  
}

locator.dart

import 'package:flutter_lecture/service/album_service.dart';
import 'package:get_it/get_it.dart';

GetIt locator = GetIt.instance;

initLocator() {
  locator.registerLazySingleton<AlbumService>(() => AlbumServiceImplementation());

}

 

album_getit_view.dart

import 'package:flutter/material.dart';
import 'package:flutter_lecture/locator/locator.dart';
import 'package:flutter_lecture/model/albums.dart';
import 'package:flutter_lecture/bloc/album_bloc.dart';
import 'package:flutter_lecture/service/album_service.dart';

import '../model/album.dart';

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key});

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  final AlbumService _service = locator<AlbumService>();


  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text("test title"),
      ),
      body: FutureBuilder(
        future: _service.fetchAlbums(),
        builder: (context, snapshot) {
          if(snapshot.hasData) {
            List<Album>? list = snapshot.data;
            return ListView.builder(
                itemCount: list?.length,
                itemBuilder: (context, index) {
                  return Container(
                    padding: const EdgeInsets.all(10),
                    child: Column(
                      crossAxisAlignment: CrossAxisAlignment.start,
                      children: [
                        Text("ID: ${list?[index].id.toString()}"),
                        Text("Title: ${list?[index].title}" )
                      ],
                    ),
                  );
                }
            );
          } else if(snapshot.hasError){
            return Center(
              child: Text(snapshot.error.toString()),
            );
          } else {
            return const Center(
              child: CircularProgressIndicator(
                strokeWidth: 2,
              ),
            );
          }
        },
      ),
    );
  }
}

 

메인 메소드 : 

void main() {
  initLocator();
  runApp(const MyApp());
}

 

 

장점

 

간단함:

의존성 주입을 간단하게 구현할 수 있습니다.

유연성:

다양한 유형의 서비스 인스턴스를 등록하고 관리할 수 있습니다.

테스트 용이성:

의존성을 쉽게 주입할 수 있어 단위 테스트를 쉽게 작성할 수 있습니다.

 

참고 문서

 

Get It 패키지 문서

Flutter 공식 의존성 주입 가이드

 

이 정보를 통해 Flutter 애플리케이션에서 get_it 패턴을 사용하여 의존성 주입을 효율적으로 관리할 수 있습니다. get_it 패턴을 활용하면 코드의 구조를 개선하고 유지보수성을 높일 수 있습니다.

반응형

'프론트엔드 > Flutter' 카테고리의 다른 글

flutter webview 사용방법  (0) 2024.07.22
flutter local notifications - 알림  (0) 2024.07.22
flutter provider 패턴  (1) 2024.07.22
flutter bloc 패턴 적용  (0) 2024.07.22
flutter 리프레쉬 인디케이터  (0) 2024.07.22
반응형

Provider 패턴은 Flutter 애플리케이션에서 상태 관리를 효율적으로 수행하기 위해 사용되는 패턴입니다. 이 패턴은 InheritedWidget을 기반으로 하며, 상태를 위젯 트리에서 쉽게 공유하고 관리할 수 있게 도와줍니다. Provider 패턴은 Flutter의 공식 상태 관리 솔루션 중 하나로, 단순하고 강력한 상태 관리 방법을 제공합니다.

 

주요 개념

 

1. Provider:

상태를 제공하는 역할을 합니다. Provider는 다양한 유형이 있으며, ChangeNotifierProvider, Provider, FutureProvider, StreamProvider 등이 있습니다.

2. ChangeNotifier:

상태 변경을 알리기 위해 사용되는 클래스입니다. ChangeNotifier를 상속받아 상태를 관리하고, notifyListeners()를 호출하여 리스너들에게 상태 변경을 알립니다.

3. Consumer:

Provider에서 제공하는 상태를 구독하고, 상태가 변경될 때마다 UI를 업데이트하는 역할을 합니다.

 

Provider의 유형

 

1. ChangeNotifierProvider:

ChangeNotifier를 사용하여 상태를 제공하고 관리합니다.

2. Provider:

기본적인 상태 제공자로, 변경되지 않는 데이터를 제공할 때 사용합니다.

3. FutureProvider:

비동기 작업의 결과를 제공하는 Provider입니다.

4. StreamProvider:

스트림 데이터를 제공하는 Provider입니다.

 

예제: ChangeNotifierProvider 사용

 

간단한 카운터 애플리케이션을 Provider 패턴으로 구현한 예제를 살펴보겠습니다.


dependencies 에 추가

provider:

 

모델 클래스

import 'package:flutter/foundation.dart';

class Counter with ChangeNotifier {
  int _count = 0;

  int get count => _count;

  void increment() {
    _count++;
    notifyListeners();
  }

  void decrement() {
    _count--;
    notifyListeners();
  }
}

메인 애플리케이션

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'counter.dart'; // Counter 모델 클래스 가져오기

void main() {
  runApp(
    MultiProvider(
      providers: [
        ChangeNotifierProvider(create: (_) => Counter()),
      ],
      child: MyApp(),
    ),
  );
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: CounterPage(),
    );
  }
}

class CounterPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final counter = Provider.of<Counter>(context);

    return Scaffold(
      appBar: AppBar(
        title: Text('Provider Counter Example'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text(
              'Counter: ${counter.count}',
              style: TextStyle(fontSize: 24),
            ),
          ],
        ),
      ),
      floatingActionButton: Column(
        mainAxisAlignment: MainAxisAlignment.end,
        children: [
          FloatingActionButton(
            onPressed: counter.increment,
            child: Icon(Icons.add),
          ),
          SizedBox(height: 10),
          FloatingActionButton(
            onPressed: counter.decrement,
            child: Icon(Icons.remove),
          ),
        ],
      ),
    );
  }
}

주요 구성 요소

 

1. Counter 모델 클래스:

ChangeNotifier를 상속받아 상태를 관리합니다.

incrementdecrement 메서드를 통해 상태를 변경하고, notifyListeners()를 호출하여 리스너에게 상태 변경을 알립니다.

2. MultiProvider:

여러 Provider를 동시에 제공할 때 사용합니다. 이 예제에서는 ChangeNotifierProvider를 사용하여 Counter 상태를 제공합니다.

3. Provider.of(context):

현재 컨텍스트에서 Provider가 제공하는 상태를 구독합니다. 여기서는 Counter 상태를 구독하여 카운터 값을 업데이트합니다.

4. FloatingActionButton:

onPressed 콜백에서 counter.incrementcounter.decrement를 호출하여 카운터 값을 증가 및 감소시킵니다.

 

 

 

다른 예시(실습)

필요 파일
provider 패키지
- album_provider.dart
model 패키지
- album.dart

view 패키지

- album_provider_view.dart

 

album.dart

class Album {
  int? userId;
  int? id;
  String? title;

  Album({this.userId, this.id, this.title});

  factory Album.fromJson(Map<String, dynamic> json) {
    return Album(
      userId: json['userId'],
      id: json['id'],
      title: json['title'],
    );
  }
}

 

album_provider.dart

import 'dart:convert';

import 'package:flutter/cupertino.dart';
import 'package:http/http.dart' as http;

import '../model/album.dart';

class AlbumProvider with ChangeNotifier {
  final List<Album> _albumList = List.empty(growable: true);

  List<Album> getAlbumList() {
    _fetchAlbums();
    return _albumList;
  }

  void _fetchAlbums() async {
    final response = await http.get(Uri.parse("https://jsonplaceholder.typicode.com/albums"));
    final List<Album> result = jsonDecode(response.body)
        .map<Album>((json) => Album.fromJson(json))
        .toList();
    _albumList.clear();
    _albumList.addAll(result);
    notifyListeners();
  }
}

album_provider_view.dart

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

import '../model/album.dart';

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key});

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {

  late List<Album> albumList;

  @override
  void initState() {
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return ChangeNotifierProvider<AlbumProvider>(
      create: ((context) => AlbumProvider()),
      child: Scaffold(
        appBar: AppBar(
          title: const Text("test title"),
        ),
        body: Consumer<AlbumProvider>(
          builder: (context, provider, child) {
            albumList = provider.getAlbumList();
            return ListView.builder(
                itemCount: albumList.length,
                itemBuilder: (context, index) {
                  return Container(
                    padding: const EdgeInsets.all(10),
                    child: Text("${albumList[index].id} : ${albumList[index].title}"),
                  );
                }
            );
          },
        ),
      ),
    );
  }
}

 

 

 

Provider 패턴의 장점

 

1. 단순성:

Provider 패턴은 Flutter의 상태 관리 솔루션 중 가장 단순하면서도 강력한 패턴입니다.

2. 유연성:

다양한 유형의 Provider를 제공하여 다양한 상태 관리 요구를 충족할 수 있습니다.

3. 성능 최적화:

Provider는 효율적으로 상태를 구독하고 업데이트하여 성능을 최적화합니다.

4. 재사용성:

상태와 비즈니스 로직을 별도의 클래스로 분리하여 재사용성을 높입니다.

 

참고 문서

 

Provider 패키지 문서

Flutter 공식 상태 관리 문서

 

이 정보를 통해 Flutter 애플리케이션에서 Provider 패턴을 효과적으로 사용하여 상태 관리를 수행할 수 있습니다. Provider 패턴을 활용하면 코드의 구조를 개선하고 유지보수성을 높일 수 있습니다.

반응형

'프론트엔드 > Flutter' 카테고리의 다른 글

flutter local notifications - 알림  (0) 2024.07.22
flutter get_it 패턴  (1) 2024.07.22
flutter bloc 패턴 적용  (0) 2024.07.22
flutter 리프레쉬 인디케이터  (0) 2024.07.22
dart 반복문 정리  (0) 2024.07.21
반응형

BLoC(Business Logic Component) 패턴은 Flutter 애플리케이션에서 비즈니스 로직을 UI 코드와 분리하는 데 사용되는 설계 패턴입니다.

이 패턴은 StreamSink를 활용하여 데이터의 흐름을 관리하고, 상태 관리를 효율적으로 수행할 수 있게 합니다.

BLoC 패턴은 Flutter의 공식 상태 관리 솔루션 중 하나로, 재사용성과 테스트 가능성을 높이는 데 기여합니다.

 

 

주요 개념

 

1. BLoC (Business Logic Component):

비즈니스 로직을 담당하는 컴포넌트입니다. Stream을 통해 UI에 데이터를 전달하고, Sink를 통해 UI로부터 이벤트를 받습니다.

2. Stream:

비동기 데이터의 흐름을 나타내는 Dart의 클래스입니다. BLoC 패턴에서는 상태 변경을 UI로 전달하는 데 사용됩니다.

3. Sink:

데이터를 Stream에 전달하는 입력 통로입니다. BLoC 패턴에서는 UI에서 발생한 이벤트를 BLoC로 전달하는 데 사용됩니다.

4. Event:

UI에서 발생하는 사용자 상호작용을 나타냅니다. 버튼 클릭, 폼 입력 등 다양한 이벤트가 포함됩니다.

5. State:

UI에서 표시할 데이터를 나타냅니다. BLoC는 상태를 관리하고 Stream을 통해 UI로 전달합니다.

 

BLoC 패턴의 구성 요소

 

1. Event 클래스:

사용자가 수행하는 동작을 정의합니다.

2. State 클래스:

애플리케이션의 상태를 정의합니다.

3. BLoC 클래스:

이벤트를 처리하고 상태를 관리하는 로직을 포함합니다.

 

예제

 

간단한 카운터 애플리케이션을 BLoC 패턴으로 구현한 예제를 살펴보겠습니다.

 

Event 클래스

abstract class CounterEvent {}

class Increment extends CounterEvent {}

class Decrement extends CounterEvent {}

 

State 클래스

class CounterState {
  final int count;

  CounterState(this.count);
}

 

BLoC 클래스

import 'dart:async';

class CounterBloc {
  final _stateController = StreamController<CounterState>();
  StreamSink<CounterState> get _inCounter => _stateController.sink;
  Stream<CounterState> get counter => _stateController.stream;

  final _eventController = StreamController<CounterEvent>();
  Sink<CounterEvent> get counterEventSink => _eventController.sink;

  CounterBloc() {
    _eventController.stream.listen(_mapEventToState);
  }

  void _mapEventToState(CounterEvent event) {
    if (event is Increment) {
      _inCounter.add(CounterState(_stateController.stream.value.count + 1));
    } else if (event is Decrement) {
      _inCounter.add(CounterState(_stateController.stream.value.count - 1));
    }
  }

  void dispose() {
    _stateController.close();
    _eventController.close();
  }
}

 

UI 코드

import 'package:flutter/material.dart';
import 'counter_bloc.dart'; // BLoC 파일을 가져옴

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: CounterPage(),
    );
  }
}

class CounterPage extends StatelessWidget {
  final CounterBloc _bloc = CounterBloc();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('BLoC Counter Example'),
      ),
      body: Center(
        child: StreamBuilder<CounterState>(
          stream: _bloc.counter,
          initialData: CounterState(0),
          builder: (context, snapshot) {
            if (!snapshot.hasData) {
              return CircularProgressIndicator();
            }
            return Text(
              'Counter: ${snapshot.data.count}',
              style: TextStyle(fontSize: 24),
            );
          },
        ),
      ),
      floatingActionButton: Column(
        mainAxisAlignment: MainAxisAlignment.end,
        children: [
          FloatingActionButton(
            onPressed: () {
              _bloc.counterEventSink.add(Increment());
            },
            child: Icon(Icons.add),
          ),
          SizedBox(height: 10),
          FloatingActionButton(
            onPressed: () {
              _bloc.counterEventSink.add(Decrement());
            },
            child: Icon(Icons.remove),
          ),
        ],
      ),
    );
  }
}

 

예시(실습)

 

 

패키지 추가

rxdart:
http:



model 부터 생성해줍니다.

class Album {
  int? userId;
  int? id;
  String? title;

  Album({this.userId, this.id, this.title});

  factory Album.fromJson(Map<String, dynamic> json) {
    return Album(
      userId: json['userId'],
      id: json['id'],
      title: json['title'],
    );
  }
}
import './album.dart';

class Albums {
  late List<Album> albums;

  Albums({required this.albums});

  Albums.fromJSON(List<dynamic> json) {
    albums = List<Album>.empty(growable: true);
    for (dynamic val in json) {
      albums.add(Album.fromJson(val));
    }
  }
}

 

provider 를 생성해줍니다.

import 'dart:convert';

import 'package:http/http.dart' show Client;
import 'package:flutter_lecture/model/albums.dart';


class AlbumApiProvider {
  Client client = Client();
  
  Future<Albums> fetchAlbumList() async {
    final response = await client
        .get(Uri.parse("https://jsonplaceholder.typicode.com/albums"));

    if (response.statusCode == 200) {
      final data = jsonDecode(response.body);
      return Albums.fromJSON(data);
    } else {
      throw Exception("Failed");
    }
  }
}

 

repository 를 생성합니다.

import 'package:flutter_lecture/data_provider/api_provider.dart';
import 'package:flutter_lecture/model/albums.dart';

class AlbumRepository {

  final AlbumApiProvider _albumApiProvider = AlbumApiProvider();

  Future<Albums> fetchAllAlbums() async => _albumApiProvider.fetchAlbumList();
  
}

 

bloc 파일을 만들어줍니다. 

import 'package:flutter_lecture/repository/ablum_repository.dart';
import 'package:rxdart/rxdart.dart';

import '../model/albums.dart';

class AlbumBloc {
  final AlbumRepository _albumRepository = AlbumRepository();
  final PublishSubject<Albums> _albumFetcher = PublishSubject<Albums>();

  Stream<Albums> get allAlbums => _albumFetcher.stream;

  Future<void> fetchAllAlbums() async {
    Albums albums = await _albumRepository.fetchAllAlbums();
    _albumFetcher.sink.add(albums);
  }

  dispose() {
    _albumFetcher.close();
  }
}

 

bloc 파일을 적용할 view 파일을 마지막으로 만들어줍니다.

import 'package:flutter/material.dart';
import 'package:flutter_lecture/model/albums.dart';
import 'package:flutter_lecture/bloc/album_bloc.dart';

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key});

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  final AlbumBloc _albumBloc = AlbumBloc();


  @override
  void initState() {
    _albumBloc.fetchAllAlbums();
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text("test title"),
      ),
      body: StreamBuilder<Albums>(
        stream: _albumBloc.allAlbums,
        builder: (context, snapshot) {
          if(snapshot.hasData) {
            Albums? albumList = snapshot.data;
            return ListView.builder(
              itemCount: albumList?.albums.length,
              itemBuilder: (context, index) {
                return Container(
                  padding: const EdgeInsets.all(10),
                  child: Column(
                    crossAxisAlignment: CrossAxisAlignment.start,
                    children: [
                      Text("ID: ${albumList?.albums[index].id.toString()}"),
                      Text("Title: ${albumList?.albums[index].title}" )
                    ],
                  ),
                );
              }
            );
          } else if(snapshot.hasError){
              return Center(
                child: Text(snapshot.error.toString()),
              );

          } else {
            return const Center(
              child: CircularProgressIndicator(
                strokeWidth: 2,
              ),
            );
          }
        },
      ),
    );
  }
}

 

 

BLoC 패턴의 장점

 

1. UI와 비즈니스 로직의 분리:

UI 코드와 비즈니스 로직을 명확히 분리하여 코드의 가독성과 유지보수성을 높입니다.

2. 재사용성과 테스트 용이성:

BLoC 컴포넌트는 독립적으로 동작하므로 다른 프로젝트나 화면에서 재사용할 수 있습니다. 또한, 비즈니스 로직이 UI와 분리되어 있어 단위 테스트가 용이합니다.

3. 비동기 데이터 처리:

StreamSink를 사용하여 비동기 데이터 처리를 효과적으로 관리할 수 있습니다.

 

참고 문서

 

Bloc 패턴 공식 문서

Flutter의 상태 관리

Dart의 Stream 클래스 문서

 

이 정보를 통해 Flutter 애플리케이션에서 BLoC 패턴을 효과적으로 사용하여 상태 관리를 수행할 수 있습니다. BLoC 패턴을 활용하면 코드의 구조를 개선하고 유지보수성을 높일 수 있습니다.

반응형

'프론트엔드 > Flutter' 카테고리의 다른 글

flutter get_it 패턴  (1) 2024.07.22
flutter provider 패턴  (1) 2024.07.22
flutter 리프레쉬 인디케이터  (0) 2024.07.22
dart 반복문 정리  (0) 2024.07.21
dart 조건문 정리  (0) 2024.07.21
반응형

RefreshIndicator

RefreshIndicator는 Flutter에서 사용자가 목록을 끌어 당겨서 새로 고침(pull-to-refresh) 동작을 수행할 수 있게 하는 위젯입니다. 주로 스크롤 가능한 목록에서 사용되며, 사용자에게 데이터를 새로 고치는 기능을 제공합니다.

주요 속성

  • child: RefreshIndicator가 감싸는 스크롤 가능한 위젯입니다. 일반적으로 ListView와 같은 위젯이 사용됩니다.
  • onRefresh: 새로 고침 동작이 발생할 때 호출되는 콜백 함수입니다. 이 함수는 Future<void>를 반환하여 비동기 작업을 수행할 수 있습니다.
  • color: 인디케이터의 색상을 지정합니다.
  • backgroundColor: 인디케이터의 배경색을 지정합니다.
  • displacement: 인디케이터가 표시될 때 위쪽에서 떨어진 거리입니다.
  • strokeWidth: 인디케이터의 선 두께입니다.

사용 예제

아래는 RefreshIndicator를 사용하여 목록을 새로 고침하는 예제입니다.

import 'package:flutter/material.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('RefreshIndicator Example'),
        ),
        body: RefreshIndicatorExample(),
      ),
    );
  }
}

class RefreshIndicatorExample extends StatefulWidget {
  @override
  _RefreshIndicatorExampleState createState() => _RefreshIndicatorExampleState();
}

class _RefreshIndicatorExampleState extends State<RefreshIndicatorExample> {
  final List<String> _items = List.generate(20, (index) => 'Item ${index + 1}');

  Future<void> _refresh() async {
    await Future.delayed(Duration(seconds: 2)); // 2초 동안 대기

    setState(() {
      _items.shuffle(); // 아이템을 섞어서 목록을 새로 고침
    });
  }

  @override
  Widget build(BuildContext context) {
    return RefreshIndicator(
      onRefresh: _refresh,
      child: ListView.builder(
        itemCount: _items.length,
        itemBuilder: (context, index) {
          return ListTile(
            title: Text(_items[index]),
          );
        },
      ),
    );
  }
}

 

주요 포인트

  1. RefreshIndicator 사용:
    • RefreshIndicator 위젯은 스크롤 가능한 위젯을 감싸서 사용합니다.
    • onRefresh 콜백은 데이터를 새로 고침하는 로직을 포함해야 하며, Future<void>를 반환해야 합니다.
  2. 데이터 새로 고침:
    • onRefresh 콜백 내에서 비동기 작업을 수행하여 데이터를 새로 고칩니다.
    • 비동기 작업이 완료되면 setState를 호출하여 UI를 업데이트합니다.
  3. UI 업데이트:
    • setState를 사용하여 데이터 변경 사항을 반영하고 UI를 업데이트합니다.

추가 예제: 커스텀 인디케이터

기본적인 사용 예제 외에도 RefreshIndicator의 속성을 사용하여 커스텀 인디케이터를 만들 수 있습니다.

import 'package:flutter/material.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('Custom RefreshIndicator Example'),
        ),
        body: CustomRefreshIndicatorExample(),
      ),
    );
  }
}

class CustomRefreshIndicatorExample extends StatefulWidget {
  @override
  _CustomRefreshIndicatorExampleState createState() => _CustomRefreshIndicatorExampleState();
}

class _CustomRefreshIndicatorExampleState extends State<CustomRefreshIndicatorExample> {
  final List<String> _items = List.generate(20, (index) => 'Item ${index + 1}');

  Future<void> _refresh() async {
    await Future.delayed(Duration(seconds: 2));

    setState(() {
      _items.shuffle();
    });
  }

  @override
  Widget build(BuildContext context) {
    return RefreshIndicator(
      onRefresh: _refresh,
      color: Colors.white,
      backgroundColor: Colors.blue,
      displacement: 40,
      strokeWidth: 3,
      child: ListView.builder(
        itemCount: _items.length,
        itemBuilder: (context, index) {
          return ListTile(
            title: Text(_items[index]),
          );
        },
      ),
    );
  }
}

 

dio 를 이용한 데이터 가져오기 예시 (실습)

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

import 'product.dart';

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key});

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  final Dio dio = Dio();

  Future<List<Product>> getProductData() async {
    try {
      print("데이터 가져오기 시작");
      var response = await dio.get("https://dummyjson.com/products");
      List<Product> products = (response.data['products'] as List)
          .map<Product>((json) => Product.fromJson(json))
          .toList();
      print("데이터 가져오기 완료");
      return products;
    } catch (e) {
      print("오류 발생: $e");
      return []; // 에러 발생 시 빈 리스트 반환
    }
  }

  Future<void> refreshData() async {
    setState(() {});
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text("제품 목록"),
      ),
      body: RefreshIndicator(
        onRefresh: refreshData,
        child: FutureBuilder<List<Product>>(
          future: getProductData(),
          builder: (BuildContext context, AsyncSnapshot<List<Product>> snapshot) {
            if (snapshot.connectionState == ConnectionState.waiting) {
              return const Center(child: CircularProgressIndicator());
            } else if (snapshot.hasError) {
              return Center(child: Text('오류 발생: ${snapshot.error}'));
            } else if (!snapshot.hasData || snapshot.data!.isEmpty) {
              return const Center(child: Text('데이터가 없습니다'));
            } else {
              return ListView.builder(
                itemCount: snapshot.data!.length,
                itemBuilder: (BuildContext context, int index) {
                  var data = snapshot.data![index];
                  return Container(
                    padding: const EdgeInsets.all(10),
                    decoration: BoxDecoration(
                      border: Border.all(width: 1, color: Colors.black12),
                    ),
                    child: Text("${data.title} (${data.description})"),
                  );
                },
              );
            }
          },
        ),
      ),
    );
  }
}

결론

RefreshIndicator는 Flutter에서 사용자가 목록을 끌어 당겨서 새로 고침 동작을 수행할 수 있게 하는 유용한 위젯입니다. 이 위젯을 사용하면 사용자 경험을 향상시키고, 데이터 업데이트를 쉽게 처리할 수 있습니다. RefreshIndicator를 사용하여 간단한 pull-to-refresh 기능을 구현하고, 다양한 속성을 통해 커스터마이징할 수 있습니다.

참고 문서

반응형

'프론트엔드 > Flutter' 카테고리의 다른 글

flutter provider 패턴  (1) 2024.07.22
flutter bloc 패턴 적용  (0) 2024.07.22
dart 반복문 정리  (0) 2024.07.21
dart 조건문 정리  (0) 2024.07.21
dart collection 설명 및 예시  (0) 2024.07.21
반응형

Dart의 반복문은 특정 조건이 참일 때까지 또는 특정 횟수만큼 코드를 반복 실행하는 데 사용됩니다. Dart에서 사용할 수 있는 주요 반복문에는 for 문, for-in 문, while 문, do-while 문이 있습니다. 각 반복문과 그 사용법에 대해 설명하겠습니다.

 

1. for 문

 

for 문은 반복 횟수가 명확할 때 주로 사용됩니다. 초기화, 조건 검사, 증감식을 한 줄에 작성할 수 있습니다.

void main() {
  // 0부터 4까지 출력
  for (int i = 0; i < 5; i++) {
    print(i);
  }
}

2. for-in 문

 

for-in 문은 컬렉션(리스트, 셋 등)의 요소를 하나씩 순회할 때 사용됩니다.

void main() {
  var numbers = [1, 2, 3, 4, 5];

  // 각 요소를 출력
  for (var number in numbers) {
    print(number);
  }
}

3. while 문

 

while 문은 조건이 참인 동안 코드를 반복 실행합니다. 조건이 거짓이 되면 반복을 종료합니다.

void main() {
  int i = 0;

  // i가 5보다 작을 때까지 반복
  while (i < 5) {
    print(i);
    i++;
  }
}

4. do-while 문

 

do-while 문은 조건을 검사하기 전에 코드를 먼저 한 번 실행한 후, 조건이 참인 동안 반복 실행합니다.

void main() {
  int i = 0;

  // 최소 한 번은 실행 후, i가 5보다 작을 때까지 반복
  do {
    print(i);
    i++;
  } while (i < 5);
}

5. 중첩 반복문

 

반복문을 중첩하여 사용할 수 있습니다. 이 경우 외부 반복문이 한 번 반복될 때마다 내부 반복문이 완전히 실행됩니다.

void main() {
  // 구구단 출력
  for (int i = 1; i <= 9; i++) {
    for (int j = 1; j <= 9; j++) {
      print('$i * $j = ${i * j}');
    }
  }
}

6. 반복문 제어문

 

반복문 내에서 반복의 흐름을 제어하기 위해 breakcontinue를 사용할 수 있습니다.

 

break

 

break 문은 반복문을 즉시 종료합니다.

void main() {
  for (int i = 0; i < 5; i++) {
    if (i == 3) {
      break; // 반복문 종료
    }
    print(i);
  }
}

continue

 

continue 문은 현재 반복 주기의 나머지 부분을 건너뛰고 다음 반복 주기로 넘어갑니다.

void main() {
  for (int i = 0; i < 5; i++) {
    if (i == 3) {
      continue; // 다음 반복 주기로 건너뜀
    }
    print(i);
  }
}

요약

 

for: 초기화, 조건 검사, 증감식을 한 줄에 작성하여 반복.

for-in: 컬렉션의 요소를 순회.

while: 조건이 참인 동안 반복.

do-while: 최소 한 번 실행 후 조건이 참인 동안 반복.

중첩 반복문: 반복문 내에 다른 반복문을 포함하여 사용.

break: 반복문을 즉시 종료.

continue: 현재 반복 주기를 건너뛰고 다음 주기로 넘어감.

반응형

'프론트엔드 > Flutter' 카테고리의 다른 글

flutter bloc 패턴 적용  (0) 2024.07.22
flutter 리프레쉬 인디케이터  (0) 2024.07.22
dart 조건문 정리  (0) 2024.07.21
dart collection 설명 및 예시  (0) 2024.07.21
dart 연산자  (0) 2024.07.21
반응형

Dart의 조건문은 프로그램의 흐름을 제어하고 특정 조건에 따라 다른 코드를 실행하는 데 사용됩니다.

주요 조건문에는 if, else if, else, switch 문이 있습니다. 각 조건문과 그 사용법에 대해 설명하겠습니다.

 

1. if 문

 

if 문은 주어진 조건이 true일 때 코드를 실행합니다.

void main() {
  int number = 10;

  if (number > 5) {
    print('Number is greater than 5');
  }
}

 

2. if-else 문

 

if-else 문은 if 조건이 false일 때 else 블록의 코드를 실행합니다.

void main() {
  int number = 3;

  if (number > 5) {
    print('Number is greater than 5');
  } else {
    print('Number is not greater than 5');
  }
}

3. else if 문

 

else if 문은 여러 조건을 검사할 때 사용됩니다. 첫 번째 if 조건이 false일 때, 그 다음 else if 조건을 검사합니다.

void main() {
  int number = 7;

  if (number > 10) {
    print('Number is greater than 10');
  } else if (number > 5) {
    print('Number is greater than 5 but less than or equal to 10');
  } else {
    print('Number is 5 or less');
  }
}

4. 중첩 if 문

 

if 문은 다른 if 문 안에 중첩될 수 있습니다. 이는 복잡한 조건을 검사할 때 유용합니다.

void main() {
  int number = 8;

  if (number > 5) {
    if (number < 10) {
      print('Number is between 5 and 10');
    } else {
      print('Number is 10 or greater');
    }
  } else {
    print('Number is 5 or less');
  }
}

 

5. 삼항 연산자

 

삼항 연산자는 간단한 if-else 문을 한 줄로 표현할 수 있습니다.

void main() {
  int number = 4;
  String result = number > 5 ? 'Number is greater than 5' : 'Number is not greater than 5';
  print(result);
}

6. switch 문

 

switch 문은 하나의 변수에 대한 여러 조건을 검사할 때 사용됩니다. 각 조건은 case 키워드로 표시됩니다.

void main() {
  String grade = 'B';

  switch (grade) {
    case 'A':
      print('Excellent!');
      break;
    case 'B':
      print('Good!');
      break;
    case 'C':
      print('Fair');
      break;
    case 'D':
      print('Poor');
      break;
    default:
      print('Invalid grade');
  }
}

7. switch 문에서 fall-through 방지

 

Dart에서는 case 블록이 끝나면 break 문을 사용하여 switch 문을 종료해야 합니다. 그렇지 않으면 다음 case 블록으로 넘어가는 fall-through가 발생하지 않습니다.

void main() {
  int number = 2;

  switch (number) {
    case 1:
      print('One');
      break;
    case 2:
      print('Two');
      break;
    case 3:
      print('Three');
      break;
    default:
      print('Other number');
  }
}

요약

 

if: 조건이 참일 때 코드 실행.

if-else: 조건이 참이면 if 블록, 거짓이면 else 블록 실행.

else if: 여러 조건 검사.

중첩 if: 복잡한 조건 검사.

삼항 연산자: 간단한 if-else 문을 한 줄로 표현.

switch: 하나의 변수에 대한 여러 조건 검사.

반응형

'프론트엔드 > Flutter' 카테고리의 다른 글

flutter 리프레쉬 인디케이터  (0) 2024.07.22
dart 반복문 정리  (0) 2024.07.21
dart collection 설명 및 예시  (0) 2024.07.21
dart 연산자  (0) 2024.07.21
Dart에서 변수 초기화 null Safety 와 late 키워드  (0) 2024.07.18
반응형

Dart의 컬렉션(Collection)은 여러 요소를 관리하고 조작할 수 있는 데이터 구조를 제공합니다.

주요 컬렉션 타입으로는 리스트(List), 셋(Set), 맵(Map)이 있습니다.

각각의 컬렉션은 다양한 용도로 사용될 수 있으며, Dart는 이러한 컬렉션을 쉽게 사용할 수 있도록 다양한 메서드와 연산자를 제공합니다.

 

1. List

 

리스트(List)는 순서가 있는 요소의 집합으로, 배열과 유사합니다. 요소의 순서를 유지하며, 동일한 값을 중복해서 가질 수 있습니다.

 

생성 및 초기화

void main() {
  // 빈 리스트 생성
  List<int> numbers = [];

  // 초기값을 가지는 리스트 생성
  List<String> fruits = ['Apple', 'Banana', 'Orange'];

  // 리스트에 요소 추가
  numbers.add(1);
  numbers.add(2);
  numbers.add(3);

  // 인덱스를 사용하여 요소 접근
  print(fruits[0]); // Apple

  // 리스트 길이
  print(fruits.length); // 3
}

 

 

주요 메서드

void main() {
  List<String> fruits = ['Apple', 'Banana', 'Orange'];

  // 요소 추가
  fruits.add('Grape');

  // 여러 요소 추가
  fruits.addAll(['Mango', 'Pineapple']);

  // 요소 제거
  fruits.remove('Banana');

  // 인덱스로 요소 제거
  fruits.removeAt(1);

  // 조건에 맞는 모든 요소 제거
  fruits.removeWhere((fruit) => fruit.startsWith('A'));

  // 요소 포함 여부 확인
  print(fruits.contains('Orange')); // true

  // 요소의 인덱스 찾기
  print(fruits.indexOf('Mango')); // 2
}

 

2. Set

 

셋(Set)은 순서가 없는 고유한 요소의 집합입니다. 동일한 값을 중복해서 가질 수 없습니다.

 

생성 및 초기화

void main() {
  // 빈 셋 생성
  Set<int> numbers = {};

  // 초기값을 가지는 셋 생성
  Set<String> fruits = {'Apple', 'Banana', 'Orange'};

  // 셋에 요소 추가
  numbers.add(1);
  numbers.add(2);
  numbers.add(3);

  // 요소 추가 (중복된 요소는 추가되지 않음)
  fruits.add('Apple');

  // 셋 길이
  print(fruits.length); // 3
}

 

주요 메서드

void main() {
  Set<String> fruits = {'Apple', 'Banana', 'Orange'};

  // 요소 추가
  fruits.add('Grape');

  // 여러 요소 추가
  fruits.addAll({'Mango', 'Pineapple'});

  // 요소 제거
  fruits.remove('Banana');

  // 조건에 맞는 모든 요소 제거
  fruits.removeWhere((fruit) => fruit.startsWith('A'));

  // 요소 포함 여부 확인
  print(fruits.contains('Orange')); // true
}

 

3. Map

 

맵(Map)은 키-값 쌍으로 이루어진 컬렉션입니다. 각 키는 고유하며, 키를 통해 값에 접근할 수 있습니다.

 

생성 및 초기화

void main() {
  // 빈 맵 생성
  Map<String, int> ages = {};

  // 초기값을 가지는 맵 생성
  Map<String, int> scores = {
    'Alice': 90,
    'Bob': 85,
    'Charlie': 95,
  };

  // 맵에 요소 추가
  ages['John'] = 25;
  ages['Doe'] = 30;

  // 키를 사용하여 값 접근
  print(scores['Alice']); // 90

  // 맵 길이
  print(scores.length); // 3
}

주요 메서드

void main() {
  Map<String, int> scores = {
    'Alice': 90,
    'Bob': 85,
    'Charlie': 95,
  };

  // 요소 추가
  scores['David'] = 88;

  // 요소 제거
  scores.remove('Bob');

  // 모든 키 가져오기
  print(scores.keys); // (Alice, Charlie, David)

  // 모든 값 가져오기
  print(scores.values); // (90, 95, 88)

  // 특정 키의 값 업데이트
  scores.update('Alice', (value) => 92);

  // 키 포함 여부 확인
  print(scores.containsKey('Charlie')); // true

  // 값 포함 여부 확인
  print(scores.containsValue(88)); // true
}

결론

 

Dart의 컬렉션은 데이터를 저장하고 조작하는 데 매우 유용한 도구입니다. 리스트(List)는 순서가 있는 요소의 집합을 다루며, 셋(Set)은 고유한 요소의 집합을 다루고, 맵(Map)은 키-값 쌍을 다룹니다. 이러한 컬렉션을 사용하여 데이터를 효율적으로 관리하고 처리할 수 있습니다.

 

참고 문서

 

Dart List 클래스 문서

Dart Set 클래스 문서

Dart Map 클래스 문서

Dart Language Tour - Collections

반응형

'프론트엔드 > Flutter' 카테고리의 다른 글

dart 반복문 정리  (0) 2024.07.21
dart 조건문 정리  (0) 2024.07.21
dart 연산자  (0) 2024.07.21
Dart에서 변수 초기화 null Safety 와 late 키워드  (0) 2024.07.18
dart 3 언어 주요 변경 사항 및 새로운 기능  (0) 2024.07.18
반응형

Dart에서는 다양한 연산자를 제공하여 변수와 값에 대해 다양한 작업을 수행할 수 있습니다.

연산자는 크게 다음과 같은 범주로 나눌 수 있습니다

 

1. 산술 연산자

2. 증감 연산자

3. 관계 연산자

4. 논리 연산자

5. 비트 연산자

6. 할당 연산자

7. 조건 연산자

8. 형 변환 연산자

9. 기타 연산자

 

1. 산술 연산자

 

산술 연산자는 기본적인 산술 계산을 수행합니다.

 

+ (덧셈)

- (뺄셈)

* (곱셈)

/ (나눗셈)

~/ (몫 연산)

% (나머지 연산)

 

void main() {
  int a = 10;
  int b = 3;

  print(a + b); // 13
  print(a - b); // 7
  print(a * b); // 30
  print(a / b); // 3.3333333333333335
  print(a ~/ b); // 3
  print(a % b); // 1
}

 

2. 증감 연산자

 

증감 연산자는 변수의 값을 증가시키거나 감소시킵니다.

 

++a (전위 증가)

a++ (후위 증가)

--a (전위 감소)

a-- (후위 감소)

void main() {
  int a = 10;

  print(++a); // 11
  print(a++); // 11
  print(a); // 12
  print(--a); // 11
  print(a--); // 11
  print(a); // 10
}

 

3. 관계 연산자

 

관계 연산자는 두 피연산자 간의 관계를 비교합니다. 결과는 boolean 값입니다.

 

== (같다)

!= (같지 않다)

> (크다)

< (작다)

>= (크거나 같다)

<= (작거나 같다)

void main() {
  int a = 10;
  int b = 3;

  print(a == b); // false
  print(a != b); // true
  print(a > b); // true
  print(a < b); // false
  print(a >= b); // true
  print(a <= b); // false
}

 

4. 논리 연산자

 

논리 연산자는 boolean 값에 대해 논리 연산을 수행합니다.

 

&& (그리고)

|| (또는)

! (부정)

void main() {
  bool a = true;
  bool b = false;

  print(a && b); // false
  print(a || b); // true
  print(!a); // false
  print(!b); // true
}

 

5. 비트 연산자

 

비트 연산자는 비트 수준에서 연산을 수행합니다.

 

& (AND)

| (OR)

^ (XOR)

~ (NOT)

<< (왼쪽 시프트)

>> (오른쪽 시프트)

void main() {
  int a = 10; // 1010
  int b = 3; // 0011

  print(a & b); // 2 (0010)
  print(a | b); // 11 (1011)
  print(a ^ b); // 9 (1001)
  print(~a); // -11 (2의 보수)
  print(a << 1); // 20 (10100)
  print(a >> 1); // 5 (0101)
}

 

6. 할당 연산자

 

할당 연산자는 변수에 값을 할당하거나 특정 연산을 수행한 후 할당합니다.

 

= (할당)

+= (더한 후 할당)

-= (뺀 후 할당)

*= (곱한 후 할당)

/= (나눈 후 할당)

~/= (몫을 구한 후 할당)

%= (나머지를 구한 후 할당)

void main() {
  int a = 10;
  int b = 3;

  a += b; // a = a + b
  print(a); // 13

  a -= b; // a = a - b
  print(a); // 10

  a *= b; // a = a * b
  print(a); // 30

  a /= b; // a = a / b
  print(a); // 10.0

  a ~/= b; // a = a ~/ b
  print(a); // 3

  a %= b; // a = a % b
  print(a); // 0
}

 

7. 조건 연산자

 

조건 연산자는 특정 조건에 따라 값을 선택합니다.

 

condition ? expr1 : expr2 (삼항 연산자)

?? (널 병합 연산자)

void main() {
  int a = 10;
  int b = 3;

  var result = a > b ? 'a가 크다' : 'b가 크다';
  print(result); // a가 크다

  String? name;
  String userName = name ?? 'Guest';
  print(userName); // Guest
}

 

8. 형 변환 연산자

 

형 변환 연산자는 객체의 타입을 변환하거나 확인합니다.

 

as (형 변환)

is (타입 검사)

is! (타입 검사 - 부정)

 

void main() {
  dynamic a = 10;

  // 타입 검사
  if (a is int) {
    print('a는 int 타입입니다.');
  }

  // 타입 검사 - 부정
  if (a is! String) {
    print('a는 String 타입이 아닙니다.');
  }

  // 형 변환
  var b = a as int;
  print(b); // 10
}

 

9. 기타 연산자

 

[] (리스트 및 맵 접근 연산자)

. (멤버 접근 연산자)

?. (널 안전 멤버 접근 연산자)

.. (계단식 표기법)

void main() {
  List<int> numbers = [1, 2, 3];
  Map<String, int> ages = {'John': 25, 'Doe': 30};

  // 리스트 및 맵 접근
  print(numbers[0]); // 1
  print(ages['John']); // 25

  // 멤버 접근
  String text = 'Hello';
  print(text.length); // 5

  // 널 안전 멤버 접근
  String? nullableText;
  print(nullableText?.length); // null

  // 계단식 표기법
  var buffer = StringBuffer()
    ..write('Hello')
    ..write(' ')
    ..write('World');
  print(buffer.toString()); // Hello World
}

 

결론

 

Dart 연산자는 다양한 작업을 수행할 수 있도록 돕는 중요한 도구입니다. 연산자의 올바른 사용법을 익히면 코드의 효율성과 가독성을 높일 수 있습니다. 이 문서에서 설명한 다양한 연산자를 잘 활용하여 더욱 효과적인 Dart 프로그래밍을 수행할 수 있습니다.

 

참고 문서

 

Dart Language Tour - Operators

반응형

+ Recent posts