728x90
0. pubspec.yaml 에 기초 프로젝트 설정하기
name: culture_app
description: A new Flutter project.
publish_to: 'none'
version: 1.0.0+1
environment:
sdk: '>=3.0.0 <4.0.0'
dependencies:
flutter:
sdk: flutter
cupertino_icons: ^1.0.2
flutter_screenutil: ^5.8.4
url_launcher: ^6.1.12
cached_network_image: ^3.2.3
flutter_dotenv: ^5.1.0
font_awesome_flutter: ^10.5.0
dio: ^5.2.1+1
awesome_dialog: ^2.2.1
shimmer: ^3.0.0
intl: any
flutter_riverpod: ^2.3.6
riverpod: ^2.3.6
go_router: ^9.0.3
connectivity_plus: ^4.0.1
pretty_dio_logger: ^1.3.1
riverpod_annotation: ^2.1.1
shared_preferences: ^2.2.0
freezed_annotation: ^2.4.1
json_annotation: ^4.8.1
lottie: ^2.4.0
dev_dependencies:
flutter_test:
sdk: flutter
flutter_lints: ^2.0.0
flutter_localizations:
sdk: flutter
freezed: ^2.4.1
build_runner: ^2.4.6
flutter_launcher_icons: ^0.13.1
json_serializable: ^6.7.1
flutter:
uses-material-design: true
fonts:
- family: 'NotoSansKR'
fonts:
- asset: assets/fonts/NotoSansKR/NotoSansKR-Regular.otf
- asset: assets/fonts/NotoSansKR/NotoSansKR-Medium.otf
weight: 500
- asset: assets/fonts/NotoSansKR/NotoSansKR-Bold.otf
weight: 700
- asset: assets/fonts/NotoSansKR/NotoSansKR-Black.otf
weight: 900
assets:
- assets/images/
- assets/lotties/
# - assets/images/common/
# - assets/images/icon/
# - assets/config/
- assets/fonts/NotoSansKR/
1. main에 ProviderScope 설정하기
void main() {
runApp(const ProviderScope(child: MyApp()));
}
2. GoRouter로 화면 지정하기
import 'dart:convert';
import 'package:culture_app/detail_screen.dart';
import 'package:culture_app/home_screen.dart';
import 'package:culture_app/model/festival_model.dart';
import 'package:culture_app/onboarding_screen.dart';
import 'package:go_router/go_router.dart' show GoRoute, GoRouter;
final router = GoRouter(
routes: [
GoRoute(
path: '/',
builder: (context, state) => const OnboardingScreen(),
),
GoRoute(
path: '/home',
builder: (context, state) =>
const HomeScreen(title: 'Flutter Demo Home Page'),
),
GoRoute(
path: '/detail',
builder: (context, state) {
FestivalModel festivalModel =
FestivalModel.fromJson(state.extra as Map<String, dynamic>);
return DetailScreen(
festivalModel: festivalModel,
);
},
),
],
);
3. http 클라이언트 코드 작성하기
import 'dart:convert';
import 'package:culture_app/model/festival_model.dart';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
Future<List<FestivalModel>> getFestivalList(int year) async {
debugPrint('httpClient loading page $year');
// year = 20230711;
try {
final response =
await http.get(Uri.parse('http://3.38.247.254:3060/festivals/$year'));
print('year');
print(year);
// print response
print('response.body');
print(response.body);
final List<FestivalModel> festivalList =
(jsonDecode(response.body)['data'] as List)
.map((e) => FestivalModel.fromJson(e))
.toList();
return festivalList;
} catch (ex, st) {
debugPrint(ex.toString());
debugPrint(st.toString());
return [];
}
}
4. Riverpod :: 축제 리스트 페이징을 위한 state, provider, notifier 선언
import 'dart:async';
import 'package:culture_app/model/festival_model.dart';
import 'package:culture_app/http_client.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter/foundation.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
part 'festival_provider.freezed.dart';
// state, provider, notifier
// festival state
@freezed
abstract class FestivalState with _$FestivalState {
const factory FestivalState({
@Default(20230711) int page,
List<FestivalModel>? festivalList,
@Default(true) bool isLoading,
@Default(false) bool isLoadMoreError,
@Default(false) bool isLoadMoreDone,
}) = _FestivalState;
const FestivalState._();
}
// festival provider
final festivalProvider =
StateNotifierProvider<FestivalNotifier, FestivalState>((ref) {
return FestivalNotifier();
});
// festival notifier
class FestivalNotifier extends StateNotifier<FestivalState> {
FestivalNotifier() : super(const FestivalState()) {
_initFestival();
}
// 초기화
_initFestival([int? initPage]) async {
final page = initPage ?? state.page;
final festivalList = await getFestivalList(page);
if (festivalList == null) {
state = state.copyWith(page: page, isLoading: false);
return;
}
debugPrint('get festival is ${festivalList.length}');
state = state.copyWith(
page: page, isLoading: false, festivalList: festivalList);
}
// 추가로 불러오기 (페이징)
loadMoreFestival() async {
StringBuffer bf = StringBuffer();
bf.write('try to request loading ${state.isLoading} at ${state.page - 1}');
if (state.isLoading) {
bf.write(' fail');
return;
}
bf.write(' success');
debugPrint(bf.toString());
state = state.copyWith(
isLoading: true,
isLoadMoreDone: false,
isLoadMoreError: false,
);
final festivalList = await getFestivalList(state.page - 1);
if (festivalList == null) {
// error
state = state.copyWith(isLoadMoreError: true, isLoading: false);
return;
}
debugPrint(
'load more ${festivalList.length} posts at page ${state.page - 1}');
if (festivalList.isNotEmpty) {
// if load more return a list not empty, => increment page
state = state.copyWith(
page: state.page - 1,
isLoading: false,
isLoadMoreDone: festivalList.isEmpty,
festivalList: [...state.festivalList!, ...festivalList]);
} else {
// not increment page
state = state.copyWith(
isLoading: false,
isLoadMoreDone: festivalList.isEmpty,
);
}
}
// 새로고침
Future<void> refresh() async {
_initFestival(20230711);
}
}
5. Riverpod :: 인덱스 값 관리를 위한 state, provider, notifier 선언
import 'dart:async';
import 'package:culture_app/model/festival_model.dart';
import 'package:culture_app/http_client.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter/foundation.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
part 'onboarding_index_provider.freezed.dart';
@freezed
abstract class OnboardingIndexState with _$OnboardingIndexState {
const factory OnboardingIndexState({
@Default(0) int pageIndex,
}) = _OnboardingIndexState;
const OnboardingIndexState._();
}
final onboardingIndexProvider =
StateNotifierProvider<OnboardingIndexNotifier, OnboardingIndexState>((ref) {
return OnboardingIndexNotifier();
});
class OnboardingIndexNotifier extends StateNotifier<OnboardingIndexState> {
OnboardingIndexNotifier() : super(OnboardingIndexState()) {
_initFestival();
}
// 초기화
_initFestival() async {}
// pageIndex를 index 파라미터로 변경하는 함수
void changePageIndex(int index) {
state = state.copyWith(pageIndex: index);
}
}
6. freezed 빌드를 위한 freezed.sh 쉘 스크립트
flutter pub run build_runner build --delete-conflicting-outputs
'개발 > Flutter' 카테고리의 다른 글
Flutter :: GoRouter (0) | 2023.08.29 |
---|---|
Flutter :: Riverpod 개념, ProviderScrope, Model, State, StateNotifierProvider, Notifier, ConsumerStatefulWidget, ConsumerState, ret.watch() (0) | 2023.08.29 |
Flutter :: Color 저장해서 변수로 재사용하기 (0) | 2023.08.14 |
Flutter :: TextStyle 저장하여 관리하기 (0) | 2023.08.14 |
Flutter :: API 통신 모듈화하기 / Dio helper / JWT token / cache interceptor (0) | 2023.01.31 |