iOS Build를 하거나 앱스토어 등록을 위해 Achive 할때 Xcode 에서 generatedpluginregistrant.m 파일에 module not found 에러가 발생했습니다. module 중 가장 상위의 module을 찾을 수 없다 하여 모듈문제인줄 알고 오랫동안 찾아다녔네요.. 해결방법 공유합니다.

 

 

아래 순서대로 하나씩 따라해보면서 해결해봅시다.

 

 

1. Xcode로 Open한 Project가 Runner.xcworkspace 인지 확인

Runner.xcodeproj 로 열었다면 당장 끄고 같은 폴더 내에 Runner.xcworkspace로 열어서 다시 시도해봅시다

 

 

 

2. 프로젝트/iOS/Podfile 주석과 버전 확인

platform :ios, '12.0' 부분이 주석처리되었다면 활성화해주시고 Xcode - Runner - Build Settings - Deployment - iOS Deployment Target 과 버전을 일치시켜줍시다.

프로젝트/iOS/Podfile

 

Xcode - Runner - Build Settings - Deployment - iOS Deployment Targe

 

Runner - General - Minimum Deployments에 iOS 버전도 함께 확인하시기바랍니다. 최소버전이 더 높으면 말이 안되니까요

 

3. 다시 빌드

flutter clean
flutter pub get
cd ios
pod install

 

한번 다시 시도해봅시다.

 

 

4.  다른방법들

 

Podfile 새로 받아보자

# Uncomment this line to define a global platform for your project
platform :ios, '12.0'

# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
ENV['COCOAPODS_DISABLE_STATS'] = 'true'

project 'Runner', {
  'Debug' => :debug,
  'Profile' => :release,
  'Release' => :release,
}

def flutter_root
  generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__)
  unless File.exist?(generated_xcode_build_settings_path)
    raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first"
  end

  File.foreach(generated_xcode_build_settings_path) do |line|
    matches = line.match(/FLUTTER_ROOT\=(.*)/)
    return matches[1].strip if matches
  end
  raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get"
end

require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root)

flutter_ios_podfile_setup

target 'Runner' do
  use_frameworks!
  use_modular_headers!

  flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
  target 'RunnerTests' do
    inherit! :search_paths
  end
end

post_install do |installer|
  installer.pods_project.targets.each do |target|
    flutter_additional_ios_build_settings(target)
  end
end

 

제꺼 Podfile 복사 붙혀넣기 하고 버전수정해서 다시 시도해봅시다.

 

 

Edit Scheme 에서 Build Configuration을 변경해보자

Xcode 중앙 상단에 Runner를 클릭해서 Edit Scheme... 를 클릭하고 Info - Build Configuration 설정을 확인해보자

Debug로 되어있다면 얼른 Release로 변경하고 다시 시도해봅시다.

 

 

 

다른 방법으로 해결했다면 댓글로 방법 알려주세요.

 

오류 메세지

Warning: Pub installs executables into $HOME/.pub-cache/bin, which is not on your path.
You can fix that by adding this to your shell's config file (.bashrc, .bash_profile, etc.):

  export PATH="$PATH":"$HOME/.pub-cache/bin"

 

 

dart pub global activate rename

bundle id를 변경하던 와중 오류가 발생했습니다. 이 오류를 해결해봅시다.

 

 

 

 

해결방법

open ~/.zshrc

터미널에서 위와같이 입력해줍니다.

 

 

export PATH="$PATH":"$HOME/.pub-cache/bin"

맨 밑줄에 이렇게 입력하고 저장해줍니다.

 

 

source ~/.zshrc

이렇게 입력해서 변경된 내용을 적용시켜줍니다.

 

 

 

끝!

개요

오늘은 Flutter로 도넛차트를 만들어보도록 하겠습니다.

 

 

디자인

 

 먼저 피그마로 도넛차트의 모양을 만들어보았습니다. 마음에 들어서 이 디자인을 사용하도록 하겠습니다.

 

 

 

 

DonutChart

class DonutChart extends StatefulWidget {
  
  final double radius;
  final double strokeWidth;
  final double total;
  final double value;
  Widget? child;

  DonutChart({
    super.key,
    this.radius = 100,
    this.strokeWidth = 20,
    required this.total,
    required this.value,
    this.child,
  });

  @override
  State<DonutChart> createState() => _DonutChartState();
}

class _DonutChartState extends State<DonutChart> {
  
  @override
  Widget build(BuildContext context) {
    return const Placeholder();
  }
  
}

 

먼저 도넛모양의 차트에 데이터는 위와같이 잡았습니다.

radius : 차트의 크기 default 100

strokeWidth : 차트의 width default 20

total : 전체 합

value : 표시할 값

 

 

다음은 애니메이션을 사용할것이기 때문에

SingleTickerProviderStateMixin

를 사용하고 AnimationController와 Animation<double> 을 만들어줍니다.

 

class _DonutChartState extends State<DonutChart> with SingleTickerProviderStateMixin {
  
  late AnimationController _controller;
  late Animation<double> _valueAnimation;
  
  @override
  void initState() {
    _controller = AnimationController(
      vsync: this,
      duration: const Duration(seconds: 1)
    );
    
    super.initState();
  }
  
  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return const Placeholder();
  }

}

 

그리고  valueAnimation을 마저 구현해주고 AnimationController를 forward 해줍니다.

 

  @override
  void initState() {
    _controller = AnimationController(
      vsync: this,
      duration: const Duration(seconds: 1)
    );

    final curvedAnimation = CurvedAnimation(
        parent: _controller,
        curve: Curves.fastOutSlowIn
    );

    _valueAnimation = Tween<double>(
      begin: 0,
      end: (widget.value / widget.total) * 360
    ).animate(_controller);

    _controller.forward();

    super.initState();
}

 

 

 

CustomPainter

class _DonutChartProgressBar extends CustomPainter {

  final double strokeWidth;
  final double valueProgress;

  _DonutChartProgressBar({super.repaint, required this.strokeWidth, required this.valueProgress});

  
  @override
  void paint(Canvas canvas, Size size) {
    
    Paint defaultPaint = Paint()
      ..color = Color(0xFFE1E1E1)
      ..strokeWidth = strokeWidth
      ..style = PaintingStyle.stroke
      ..strokeCap = StrokeCap.round;
    
    Paint valuePaint = Paint()
        ..color = Color(0xFF7373C9)
        ..strokeWidth = strokeWidth
        ..style = PaintingStyle.stroke
        ..strokeCap = StrokeCap.round;
    
    Offset center = Offset(size.width / 2, size.height / 2);
    
    canvas.drawArc(
      Rect.fromCircle(center: center, radius: size.width / 2), 
      math.radians(-90), 
      math.radians(360), 
      false, 
      defaultPaint
    );

    canvas.drawArc(
        Rect.fromCircle(center: center, radius: size.width / 2),
        math.radians(-90),
        math.radians(valueProgress),
        false,
        valuePaint
    );
  }

  @override
  bool shouldRepaint(covariant CustomPainter oldDelegate) {
    return true;
  }

}

 

CustomPainter를 상속받아서 paint 해줍니다.

배경이 되는 defaultPaint 를 만들고, value 값을 표현하는 valuePaint를 만들어줍니다.

그리고 canvas에 그려주면 되는데 math는 

import 'package:vector_math/vector_math.dart' as math;

vector_math 를 임포트해주어야 사용할 수 있습니다.

 

drawArc의 첫번째 인자는 중심이 되는 지점을 표현한거고

두번째는 시작지점입니다. 3시방향이 0도이기때문에 12시 방향부터 시작하고싶어서 -90를 넣어주었습니다.

세번째는 마지막지점입니다. valueProgress 값을 사용해 외부에서 넣어주겠습니다.

네번째는 시작지점과 마지막지점을 중앙점과 이을건지 여부인데 true를 하게되면 Pie Chart가 됩니다. 저는 Donut Chart를 만들것이기 때문에 false를 해주었습니다.

마지막은 Paint를 넣어주면 됩니다.

 

shouldRepaint는 true로 하면됩니다.

 

자 이제 돌려보면

 

 

짜잔 완성했습니다. 어우 이거 만드는것보다 영상촬영하고 짤로 만드는게 더 어렵네요...

 

 

완성

DonutChart(
    radius: 50,
    strokeWidth: 10,
    total: 100,
    value: 85,
    child: Center(
      child: Text('85%',
        style: TextStyle(
          color: Colors.black54,
          fontWeight: FontWeight.w600,
          fontSize: 21
        ),
      ),
    ),
)

이렇게 완성했습니다. CustomPainter 나 Animation을 사용하는게 조금 쉽지 않았는데 만들고보니 뿌듯하네요

개요

Flutter에서 위젯의 크기를 제어하는 방법은 다양합니다. 그 중 가장 많이 사용되는 ConstrainedBox와 SizedBox의 차이점과 각각의 사용 사례에 대해 알아보겠습니다.

SizedBox의 이해

 

기본 사용법

// 고정 크기 지정
SizedBox(
  width: 100,
  height: 50,
  child: Container(
    color: Colors.blue,
  ),
)

// 간격 생성
SizedBox(height: 10)  // 세로 간격
SizedBox(width: 10)   // 가로 간격

// 최대 크기로 확장
SizedBox.expand(
  child: Container(
    color: Colors.blue,
  ),
)

 

SizedBox의 특징

  • 정확한 크기를 지정할 때 사용
  • 간단한 간격을 만들 때 유용
  • child가 없을 경우 빈 공간으로 사용
  • 성능상 가장 가벼운 위젯 중 하나

 

 

ConstrainedBox의 이해

 

기본 사용법

ConstrainedBox(
  constraints: BoxConstraints(
    minWidth: 100,
    maxWidth: 200,
    minHeight: 50,
    maxHeight: 100,
  ),
  child: Container(
    color: Colors.blue,
  ),
)

// 유용한 생성자들
ConstrainedBox(
  constraints: BoxConstraints.tightFor(width: 100, height: 100),
  child: Container(color: Colors.blue),
)

ConstrainedBox(
  constraints: BoxConstraints.loose(Size(200, 100)),
  child: Container(color: Colors.blue),
)

 

ConstrainedBox의 특징

  • 최소/최대 크기 제약 설정 가능
  • 자식 위젯의 크기를 유연하게 제어
  • 복잡한 레이아웃 제약 조건 설정 가능

 

 

실제 사용 예시

 

SizedBox 활용

class MyWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        Text('First Item'),
        SizedBox(height: 16),  // 간격 추가
        Container(
          color: Colors.blue,
          child: SizedBox(
            width: 100,
            height: 100,
            child: Center(
              child: Text('Fixed Size'),
            ),
          ),
        ),
        SizedBox(height: 16),  // 간격 추가
        Text('Last Item'),
      ],
    );
  }
}

 

 

ConstrainedBox 활용

class FlexibleWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        ConstrainedBox(
          constraints: BoxConstraints(
            minHeight: 50,
            maxHeight: 200,
          ),
          child: Container(
            color: Colors.green,
            child: Text('This box can grow between 50 and 200'),
          ),
        ),
        ConstrainedBox(
          constraints: BoxConstraints.tightFor(
            width: double.infinity,
            height: 100,
          ),
          child: Card(
            child: Center(
              child: Text('Full width, fixed height'),
            ),
          ),
        ),
      ],
    );
  }
}

 

주요 차이점과 선택 기준

 

SizedBox 사용 시나리오

정확한 크기가 필요할 때

// 고정 크기 버튼
SizedBox(
  width: 200,
  height: 50,
  child: ElevatedButton(
    onPressed: () {},
    child: Text('Fixed Size Button'),
  ),
)

 

간단한 간격이 필요할 때

Column(
  children: [
    Text('Item 1'),
    SizedBox(height: 8),  // 작은 간격
    Text('Item 2'),
    SizedBox(height: 16), // 중간 간격
    Text('Item 3'),
    SizedBox(height: 24), // 큰 간격
    Text('Item 4'),
  ],
)

 

 

ConstrainedBox 사용 시나리오

유동적인 크기 제약이 필요할 때

ConstrainedBox(
  constraints: BoxConstraints(
    minWidth: 100,
    maxWidth: MediaQuery.of(context).size.width * 0.8,
    minHeight: 50,
  ),
  child: Container(
    padding: EdgeInsets.all(16),
    child: Text('This box adapts to content'),
  ),
)

 

반응형 레이아웃 구현 시

ConstrainedBox(
  constraints: BoxConstraints(
    maxWidth: 600,  // 태블릿/데스크톱에서 최대 너비 제한
  ),
  child: ListView(
    children: [
      // 리스트 아이템들
    ],
  ),
)

 

 

성능 고려사항

 

SizedBox

// 권장: 간단하고 효율적
SizedBox(width: 100, height: 100)

// 비권장: 불필요한 중첩
Container(
  width: 100,
  height: 100,
)

 

ConstrainedBox

// 권장: 필요한 경우만 사용
ConstrainedBox(
  constraints: BoxConstraints(maxWidth: 200),
  child: Text('Limited width text'),
)

// 비권장: 불필요한 제약 사용
ConstrainedBox(
  constraints: BoxConstraints.tightFor(width: 100, height: 100),
  child: Container(),  // SizedBox를 사용하는 것이 더 효율적
)

 

 

마치며

  • SizedBox는 정확한 크기나 간격이 필요할 때 사용
  • ConstrainedBox는 유연한 크기 제약이 필요할 때 사용
  • 성능을 고려할 때는 가능한 한 간단한 위젯을 선택
  • 레이아웃의 목적과 요구사항에 따라 적절한 위젯 선택이 중요

개요

기본적으로 부모 위젯에서 자식 위젯의 메소드를 호출할 수 있는 방법은 따로 없습니다. 하지만 불가능한것은 아니죠. 오늘은 그것에 대해서 알아보도록 하겠습니다.
 
 
 
 

Controller

부모 - 자식 위젯간에 Controller를 하나 두는 방법입니다. 부모 위젯에서 Controller를 생성하고 자식 위젯에게 전달해 메소드를 주입받는 방식이죠.
 

class TimerController {

  late Function() start;
  late Function() stop;

}

 
먼저 이렇게 Controller를 만듭니다.
 

class MainPage extends StatefulWidget {
  const MainPage({super.key});
  
  @override
  State<MainPage> createState() => _MainPageState();
}

class _MainPageState extends State<MainPage> {

  final TimerController _timerController = TimerController();

  @override
  Widget build(BuildContext context) {
    return CustomTimerWidget(
      controller: _timerController,
    );
  }
}

 
부모위젯에서 자식위젯에 TImerController를 주입해줍니다.
 

class CustomTimerWidget extends StatefulWidget {

  TimerController? controller;
  CustomTimerWidget({super.key, this.controller});

  @override
  State<CustomTimerWidget> createState() => _CustomTimerWidgetState();
}

class _CustomTimerWidgetState extends State<CustomTimerWidget> {
  
  void start() {
    print('timer start');
  }
  void stop() {
    print('timer stop');
  }
  _timerInit() {
    widget.controller?.start = start;
    widget.controller?.stop = stop;
  }
  
  @override
  void initState() {
    _timerInit();
    super.initState();
  }
  
  @override
  Widget build(BuildContext context) {
    return const Placeholder();
  }
}

 
자식 위젯에서 initState 시점에 부모로 부터 받은 controller에 Function을 넣어줍니다.
이제 부모위젯에서 자식 위젯의 메소드를 사용할 수 있게되었습니다.
 
 
이 방법은 flutter내에서 정말 많이 사용하고 있는 패턴같습니다. PageView, ListView 등등 다양한곳에서 Controller를 받는걸 볼 수 있습니다. 이 방법말고 GlobalKey를 이용해 자식위젯의 메소드를 사용할 수 는 있지만 같은 파일(.dart) 내에 존재하지 않는다면 사용 할 수 없다는 큰 단점이 있고, GlobalKey를 많이 사용할 수록 복잡해지는 단점이있어서 위와 같은 방식을 추천드립니다.

FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ':app:mergeReleaseResources'.
> Multiple task action failures occurred:
   > A failure occurred while executing com.android.build.gradle.internal.res.Aapt2CompileRunnable
      > Android resource compilation failed
        ERROR:/Users/user/app/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: AAPT: error: file failed to compile.

   > A failure occurred while executing com.android.build.gradle.internal.res.Aapt2CompileRunnable
      > Android resource compilation failed
        ERROR:/Users/user/app/android/app/src/main/res/mipmap-mdpi/ic_launcher.png: AAPT: error: file failed to compile.

   > A failure occurred while executing com.android.build.gradle.internal.res.Aapt2CompileRunnable
      > Android resource compilation failed
        ERROR:/Users/user/app/android/app/src/main/res/mipmap-hdpi/ic_launcher.png: AAPT: error: file failed to compile.

   > A failure occurred while executing com.android.build.gradle.internal.res.Aapt2CompileRunnable
      > Android resource compilation failed
        ERROR:/Users/user/app/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: AAPT: error: file failed to compile.

   > A failure occurred while executing com.android.build.gradle.internal.res.Aapt2CompileRunnable
      > Android resource compilation failed
        ERROR:/Users/user/app/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: AAPT: error: file failed to compile.


* Try:
> Run with --stacktrace option to get the stack trace.
> Run with --info or --debug option to get more log output.
> Run with --scan to get full insights.

* Get more help at https://help.gradle.org

BUILD FAILED in 9s
Running Gradle task 'assembleRelease'...                           10.2s
Gradle task assembleRelease failed with exit code 1

 

 

앱 아이콘을 변경하고 이런 오류가 발생했습니다.

이미지 파일에도 문제가 없는데 말이죠...

 

앱 아이콘은 반드시 png 파일로 되어있어야 합니다.

 

그런데 이름만 png이지 실제 확장자는 JPEG로 되어있더군요

 

 

jpeg -> png 로 변환하고 다시 build하니 잘 되었습니다.

플러터 앱 아이콘 변경하는 법

 

flutter_launcher_icons 패키지를 통해 쉽게 바꿀 수 있지만 패키지를 사용하기 전에 직접 바꾸는 법을 알아보도록 하겠습니다.

 

flutter_launcher_icons | Dart package

A package which simplifies the task of updating your Flutter app's launcher icon.

pub.dev

 

 

 

 

아이콘 만들기

 

 

App Icon Generator

 

www.appicon.co

 

 

이미지를 업로드하고 원하는 플랫폼을 선택한 후에 Generate를 누르면

 

 

이렇게 파일이 생성됩니다.

 

 

 

 

안드로이드

android/app/src/main/res 에 들어가면

이런 데이터들이 있는데 이곳에다가 아까 다운받은 아이콘안에 android 폴더 내부데이터를 모두 이곳으로 옮겨줍니다.

 

 

 

 

iOS

ios/Runner/Assets.xcassets 폴더에 Assets.appiconset 폴더가 있는데 이것도 마찬가지로 다운받은 폴더에 Assets.appiconset 를 덮어쓰기 해주시면됩니다.

 

iOS

 

끝!

플러서 앱 이름 바꾸는 법

 

 

안드로이드

app/src/main/AndroidManifest.xml 에 들어가서

<application
    android:label="앱 이름"

label에 이름을 변경하면 됩니다.

 

 

iOS

ios/Runner/Info.plist 에 들어가서

<key>CFBundleDisplayName</key>
<string>앱 이름</string>

 

CFBundleDisplayName 을 찾고 아래 string 에 이름을 변경하면 됩니다.

 

iOS

 

StatefulWidget의 생명주기 메서드

 

createState()

class MyWidget extends StatefulWidget {
  @override
  _MyWidgetState createState() => _MyWidgetState();  // 가장 먼저 호출
}

 

initState()

class _MyWidgetState extends State<MyWidget> {
  late StreamSubscription _subscription;
  
  @override
  void initState() {
    super.initState();  // 반드시 super 호출
    
    // 초기화 작업 수행
    _subscription = Stream.periodic(Duration(seconds: 1))
        .listen((data) => print('Stream data: $data'));
  }
}

 

didChangeDependencies()

class _MyWidgetState extends State<MyWidget> {
  ThemeData? _theme;
  
  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    
    // 상위 위젯의 데이터 의존성 변경 시 호출
    _theme = Theme.of(context);  // InheritedWidget 데이터 접근
  }
}

 

 

build()

class _MyWidgetState extends State<MyWidget> {
  @override
  Widget build(BuildContext context) {
    return Container(
      child: Text('Widget Content'),
    );
  }
}

 

 

didUpdateWidget()

class _MyWidgetState extends State<MyWidget> {
  @override
  void didUpdateWidget(MyWidget oldWidget) {
    super.didUpdateWidget(oldWidget);
    
    // 위젯이 업데이트될 때 호출
    if (widget.someProperty != oldWidget.someProperty) {
      // 속성 변경에 따른 처리
    }
  }
}

 

 

dispose()

class _MyWidgetState extends State<MyWidget> {
  late StreamSubscription _subscription;
  
  @override
  void dispose() {
    // 리소스 해제
    _subscription.cancel();
    super.dispose();  // 반드시 마지막에 super 호출
  }
}

 

 

생명주기 단계별 주의사항

 

initState

  • super.initState() 반드시 먼저 호출
  • context 사용 불가 (didChangeDependencies에서 사용)
  • 비동기 작업 시작 가능
  • 한 번만 호출됨

didChangeDependencies

  • InheritedWidget 데이터 접근
  • 여러 번 호출될 수 있음
  • 상위 위젯의 데이터 변경 시 호출

build

  • 순수 함수여야 함
  • 상태 변경 금지 (setState 호출 금지)
  • 가능한 가벼운 연산만 수행

dispose

  • 리소스 해제 필수
  • super.dispose() 마지막에 호출
  • mounted 체크 후 setState 호출

 

일반적인 실수와 해결방법

 

메모리 누수 방지

class _MyWidgetState extends State<MyWidget> {
  StreamSubscription? _subscription;
  
  @override
  void initState() {
    super.initState();
    _subscription = Stream.periodic(Duration(seconds: 1))
        .listen((data) {
          if (!mounted) return;  // mounted 체크
          setState(() {
            // 상태 업데이트
          });
        });
  }
  
  @override
  void dispose() {
    _subscription?.cancel();
    super.dispose();
  }
}

 

비동기 작업 처리

class _MyWidgetState extends State<MyWidget> {
  @override
  void initState() {
    super.initState();
    _loadData();
  }
  
  Future<void> _loadData() async {
    try {
      await Future.delayed(Duration(seconds: 2));
      if (!mounted) return;
      setState(() {
        // 상태 업데이트
      });
    } catch (e) {
      print('Error: $e');
    }
  }
}

 

 

Context 사용

class _MyWidgetState extends State<MyWidget> {
  @override
  void initState() {
    super.initState();
    // Error: context 사용 불가
    // Theme.of(context);
  }
  
  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    // OK: context 사용 가능
    final theme = Theme.of(context);
  }
}

 

 

마치며

위젯의 생명주기를 잘 이해하고 적절히 활용하면 메모리 누수를 방지하고 효율적인 상태 관리가 가능합니다. 특히 dispose() 메서드에서의 리소스 해제와 비동기 작업 시의 mounted 체크는 매우 중요합니다.

'Language > Flutter' 카테고리의 다른 글

[Flutter] 앱 아이콘 변경  (0) 2024.11.22
[Flutter] 앱 이름 변경  (0) 2024.11.21
[Dart] fold() 메소드  (0) 2024.11.19
[Flutter] 상태 관리 기초 - setState  (0) 2024.11.18
[Dart] Spread 연산자(...)  (0) 2024.11.17

개요

오늘은 List의 fold() 메소드에 대해서 빠르게 알아봅시다.

 

  T fold<T>(T initialValue, T combine(T previousValue, E element)) {
    var value = initialValue;
    for (E element in this) value = combine(value, element);
    return value;
  }

 

생신건 이렇게 생겼습니다. JAVA만 파던 저에게는 뭐하는 메소드인지 감이 잘 안잡혔습니다. 함께 알아보도록 하죠.

 

 

fold()

fold는 initialValue을 시작으로 combine 값을 연산하는 방식입니다.

 

간단하게 수익률을 계산하는 메소드를 만들어보겠습니다.

void main() {

  double money = 1000000;

  List<double> rate = [5, -3.4, 0, 1.7, -4.5];

  double result = rate.fold(money,
      (previousValue, element) => previousValue + (previousValue * (element / 100)),);

}

 

fold 첫번째 인자에는 최초 시작값을 정해줍니다. 초기자금인 1,000,000원을 최초 시작값으로 설정합니다.

두번째 인자에는 T combine(T previousValue, E element) 를 넣어줍니다.

previousValue는 이전 계산된 값이고, element는 List에서 현재 인덱스가 가리키고있는 값을 의미합니다.

즉 fold가 첫번째 iterate 를 예를 들면 1000000 + (1000000 * (5 / 100)) 가 되는 것입니다.

 

 

오늘은 간단하게 fold 메소드에 대해서 알아보았습니다.

'Language > Flutter' 카테고리의 다른 글

[Flutter] 앱 이름 변경  (0) 2024.11.21
[Flutter] 위젯 생명주기(Lifecycle)  (0) 2024.11.20
[Flutter] 상태 관리 기초 - setState  (0) 2024.11.18
[Dart] Spread 연산자(...)  (0) 2024.11.17
[Dart] Null Safety  (0) 2024.11.16

+ Recent posts