controller가 singleton 방식인 것을 활용

기존

controller

class CountControllerWithGetX extends GetxController {
  int count = 0;
  void increase(String id) {
    count++;
    update([id]);
  }
}

contoller에 접근

Get.find<CountControllerWithGetX>().increase();

활용

controller에 state으로 get to 추가

class CountControllerWithGetX extends GetxController {
  static CountControllerWithGetX get to => Get.find();
  int count = 0;
  void increase(String id) {
    count++;
    update([id]);
  }
}

controller.to 로 접근 가능

CountControllerWithGetX.to.increase();

controller가 reactive 방식일때 stateless 대신 getview를 사용해서 접근

stateless -> GetView로 변경

// stateless -> GetView로 변경
class BindingPage extends GetView<CountControllerWithReactive> {
  const BindingPage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(),
      body: Column(
        children: [
          Text(
            "GetX",
            style: TextStyle(fontSize: 50),
          ),
                    // reactive 방식이어야함
          Obx(() => Text(
                "${controller.count.value}",
                style: TextStyle(fontSize: 50),
              )),
          RaisedButton(
            onPressed: () {
              //GetView 안에 controller가 있음
              controller.increase();
            },
            child: Text(
              "+",
              style: TextStyle(fontSize: 50),
            ),
          ),
        ],
      ),
    );
  }
}

참고

https://www.youtube.com/watch?v=O1Bw-mwF9xc 

 

728x90

의존성 주입 (binding)

controller를 인스턴스화 하는 방법 ( 의존성 주입 )

put, lazyput, putasync, create 방법이 있음

binding 방법

특정 페이지 안에서 특정 페이지로 이동할때 바인딩

Get.put

  • 페이지 이동시 create, initialized 되고 페이지에서 나올때 delete()가 실행되며 메모리에서 삭제
RaisedButton(
  child: Text("GetPut"),
  onPressed: () {
    //바인딩 : 페이지로 보내주면서 사용할 컨트롤러를 주입하는 방법
    Get.to(
      GetPut(),
      binding: BindingsBuilder(() {
        Get.put(DependencyController());
        //put방식은 페이지 이동시 controller를 메모리에 올려주고 나올땐 알아서 삭제해줌
      }),
    );
  },
),

Get.lazyPut

  • 버튼을 눌러 페이지 이동했을 당시에는 create, initialized되지 않지만 사용할때 생성됨
  • 나올때는 put과 동일하게 삭제
RaisedButton(
  child: Text("Get.lazyPut"),
  onPressed: () {
    Get.to(
      GetLazyPut(),
      binding: BindingsBuilder(() {
        // controller를 사용할때 메모리에 올려줌
        // 나올땐 삭제
        Get.lazyPut<DependencyController>(
            () => DependencyController());
      }),
    );
  },
),

Get.putAsync

  • 페이지에 접근을 할때 비동기식으로 데이터를 받아오거나 가공처리를 오랫동안하고 나서 컨트롤러를 인스턴스화 할때 사용
RaisedButton(
  child: Text("Get.putAsync"),
  onPressed: () {
    Get.to(
      GetPut(),
      binding: BindingsBuilder(() {
        // 페이지에 접근을 할때 비동기식으로 데이터를 받아오거나 가공처리를 오랫동안하고 나서 컨트롤러를 인스턴스화 할때 사용
        Get.putAsync<DependencyController>(() async {
          // 데이터 처리 후 인스턴스화
          await Future.delayed(Duration(seconds: 5));
          return DependencyController();
        });
      }),
    );
  },
),

Get.create

  • 페이지 이동 할 당시에는 생성 안되지만 사용할때 마다 생성됨
  • 자동으로 삭제되지 않기 때문에 사용 종료때마다 삭제 해줘야함
RaisedButton(
  child: Text("Get.create"),
  onPressed: () {
    // 인스턴스를 여러개 생성
    Get.to(GetPut(), binding: BindingsBuilder(() {
      // 사용할때성 마다 인스턴스 생성
      Get.create<DependencyController>(
          () => DependencyController());
    }));
  },
),

앱이 실행될 때 가장 앞에서 바인딩 설정

  • 라우팅 설정의 GetPage안에서 설정
getPages: [
  GetPage(
    name: '/binding',
    page: () => BindingPage(),
    binding: BindingsBuilder(() {
      Get.put(CountControllerWithGetX());
    }),
  ),
],

Bindings를 implements한 클래스를 만들어서 getPages의 binding에 넣어줄 수 있음

class BindingPageBinding implements Bindings {
  @override
  void dependencies() {
    // TODO: implement dependencies
    Get.put(CountControllerWithGetX());
  }
}
GetPage(
  name: '/binding',
  page: () => BindingPage(),
  binding: BindingPageBinding(),
),

참고

https://www.youtube.com/watch?v=KDTjo-6jFKs 

 

728x90

값이 변경되어야만 update (rebuild)를 합니다.

현재 상태값과 변경될 상태값이 동일하다면 wiget을 다시 그리지 않습니다.

controller

class CountControllerWithReactive {
  RxInt count = 0.obs; // 반응형 상태관리
  void increase() {
    count++;
  }

    // 값을 넣어 줄때
  void putNumber(int value) {
    count(value);
  }
}

ui

class ReactiveSimpleStatePage extends StatelessWidget {
  const ReactiveSimpleStatePage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    Get.put(CountControllerWithReactive());
    return Scaffold(
        appBar: AppBar(
          title: Text('반응형 상태 관리'),
        ),
        body: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              Text(
                "GetX",
                style: TextStyle(fontSize: 50),
              ),
                            // 현재 값이 5인 경우 putNumber(5)를 아무리 눌러도 다시 그려지지 않음
              Obx(() => Text(
                    "${Get.find<CountControllerWithReactive>().count.value}",
                    style: TextStyle(fontSize: 50),
                  )),
              RaisedButton(
                onPressed: () {
                  Get.find<CountControllerWithReactive>().increase();
                },
                child: Text(
                  "+",
                  style: TextStyle(fontSize: 50),
                ),
              )
              RaisedButton(
                onPressed: () {
                  Get.find<CountControllerWithReactive>().putNumber(5);
                },
                child: Text(
                  "5로 변경",
                  style: TextStyle(fontSize: 50),
                ),
              ),
            ],
          ),
        ));
  }
}

이벤트 트리거

controller

import 'package:get/get.dart';

// trigger를 위해 getxcontroller 상속
class CountControllerWithReactive extends GetxController {
  RxInt count = 0.obs; // 반응형 상태관리
  void increase() {
    count++;
  }

  void putNumber(int value) {
    count(value);
  }

  // getxcontroller lifecycle
  @override
  void onInit() {
    // TODO: implement onInit
    // worker
    // 매번 값이 변경될 때 마다 호출 (반응 상태일때만 가능)
    ever(count, (_) => print("매번 호출"));
    // 한번만 호출
    once(count, (_) => print("한번만 호출"));
    // 이벤트가 끝났을때 실행
    debounce(count, (_) => print("마지막 변경에 한번만 호출"), time: Duration(seconds: 1));
    // 변경되고 있는 동안 설정한 초마다 실행
    interval(count, (_)=>print("변경되고 있는 동안 1초마다 호출"),time: Duration(seconds: 1));
    super.onInit();
  }

  @override
  void onClose() {
    // TODO: implement onClose
    super.onClose();
  }
}

custom Rx 타입

enum 타입

enum NUM {FIRST, SECOND}
class controller {
    Rx<NUM> nums = NUM.FIRST.obs;

    void put(){
        nums(NUM.SECOND);
    }
}

class 타입

class User{
    String name;
    int age;
}
class controller {
    Rx<User> user = User().obs;

    void put(){
        user(User());
        user.update((_user){
            _user.name = 'name';
        });
    }
}

list 타입

class controller {
    RxList<String> list = [].obs;
    void put(){
        list.addAll();
        list.add();
        list.addIf(user.value.name=='name','okay'); // 조건, 대입값
    }
}

 

참고

https://www.youtube.com/watch?v=TjC1ka8fZJw 

 

728x90

단순 상태 관리 앱 화면

main(controller 등록)

class SimpleStatePage extends StatelessWidget {
  const SimpleStatePage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
        // getx controller 등록
        Get.put(CountControllerWithGetX());
    return Scaffold(
        appBar: AppBar(
          title: Text('단순 상태 관리'),
        ),
        body: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              Expanded(child: WithGetX()),
              Expanded(
                                    // provider controller 등록
                  child: ChangeNotifierProvider<CountControllerWithProvider>(
                create: (_) => CountControllerWithProvider(),
                child: WithProvider(),
              )),
            ],
          ),
        ));
  }
}

Provider

controller

class CountControllerWithProvider extends ChangeNotifier {
  int count = 0;
  void increase() {
    count++;
    notifyListeners();
  }
}

ui

class WithProvider extends StatelessWidget {
  const WithProvider({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Center(
      child: Column(
        children: [
          Text(
            "Provider",
            style: TextStyle(fontSize: 50),
          ),
          Consumer<CountControllerWithProvider>(
            builder: (_, snapshot, child) {
              return Text("${snapshot.count}", style: TextStyle(fontSize: 50));
            },
          ),
          RaisedButton(
            onPressed: () {
              // listen: false를 줘서 consumer 부분만 rebuild
              Provider.of<CountControllerWithProvider>(context, listen: false)
                  .increase();
            },
            child: Text(
              "+",
              style: TextStyle(fontSize: 50),
            ),
          ),
        ],
      ),
    );
  }
}

GetX

controller

class CountControllerWithGetX extends GetxController {
  int count = 0;
  void increase() {
    count++;
    update();
  }
}

ui

class WithGetX extends StatelessWidget {
  const WithGetX({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Center(
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          Text(
            "GetX",
            style: TextStyle(fontSize: 50),
          ),
          GetBuilder<CountControllerWithGetX>(builder: (controller) {
            return Text(
              "${controller.count}",
              style: TextStyle(fontSize: 50),
            );
          }),
          RaisedButton(
            onPressed: () {
              Get.find<CountControllerWithGetX>().increase();
            },
            child: Text(
              "+",
              style: TextStyle(fontSize: 50),
            ),
          ),
        ],
      ),
    );
  }
}

비교

Provider는 consumer를 이용해 상태값을 가져오고 provider와 context를 이용해 controller의 비즈니스 로직을 불러옵니다.

GetX는 GetBuilder를 통해 상태값을 가져오고 Get.find를 통해 controller의 비즈니스 로직을 가져옵니다.

context를 사용하지 않는 GetX 방식은 위젯으로 분리하여 사용가능합니다.

...{
    ...
    return (
    ...,
    buildRaisedButton(),
    );

    Widget buildRaisedButton() {
      return RaisedButton(
        onPressed: () {
          Get.find<CountControllerWithGetX>().increase();
        },
        child: Text(
          "+",
          style: TextStyle(fontSize: 50),
        ),
      );
    }

}

반면, provider의 경우에는 stateful 위젯으로 변경하거나 context를 넘져워야합니다.

    buildRaisedButton(context),
            ],
          ),
        );
      }

  Widget buildRaisedButton(BuildContext context) {
    return RaisedButton(
          onPressed: () {
            // listen: false를 줘서 consumer 부분만 rebuild
            Provider.of<CountControllerWithProvider>(context, listen: false)
                .increase();
          },
          child: Text(
            "+",
            style: TextStyle(fontSize: 50),
          ),
        );
  }
}

GetX는 선언이 자유롭다.

부모에서 controller를 선언해줄 필요가 없습니다.

ui 부분에서 바로 사용 가능

class WithGetX extends StatelessWidget {
  const WithGetX({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
        // 바로 사용 가능
        Get.put(CountControllerWithGetX());
    return Center(
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          Text(
            "GetX",
            style: TextStyle(fontSize: 50),
          ),
          GetBuilder<CountControllerWithGetX>(builder: (controller) {
            return Text(
              "${controller.count}",
              style: TextStyle(fontSize: 50),
            );
          }),
          RaisedButton(
            onPressed: () {
              Get.find<CountControllerWithGetX>().increase();
            },
            child: Text(
              "+",
              style: TextStyle(fontSize: 50),
            ),
          ),
        ],
      ),
    );
  }
}

또한 클래스가 생성될때 바로 사용가능합니다.

class WithGetX extends StatelessWidget {
  const WithGetX({Key? key}) : super(key: key);
    // 클래스가 생성될때 바로 사용가능
    CountControllerWithGetX _countControllerWithGetX =Get.put(CountControllerWithGetX());
  @override
  Widget build(BuildContext context) {

    return Center(
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          Text(
            "GetX",
            style: TextStyle(fontSize: 50),
          ),
          GetBuilder<CountControllerWithGetX>(builder: (controller) {
            return Text(
              "${controller.count}",
              style: TextStyle(fontSize: 50),
            );
          }),
          RaisedButton(
            onPressed: () {
                            //find를 할 필요가 없음
              _countControllerWithGetX.increase();
            },
            child: Text(
              "+",
              style: TextStyle(fontSize: 50),
            ),
          ),
        ],
      ),
    );
  }
}

특정 컨트롤러에 아이디를 부여해서 관리 가능

cotroller

class CountControllerWithGetX extends GetxController {
  int count = 0;
    // id를 받아서 update에 배열로 넣어줌
  void increase(String id) {
    count++;
    update([id]);
  }
}

ui

class WithGetX extends StatelessWidget {
  WithGetX({Key? key}) : super(key: key);
  CountControllerWithGetX _countControllerWithGetX =
      Get.put(CountControllerWithGetX());

  @override
  Widget build(BuildContext context) {
    return Center(
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          Text(
            "GetX",
            style: TextStyle(fontSize: 50),
          ),
          GetBuilder<CountControllerWithGetX>(
                        // id 추가
            id: "first",
            builder: (controller) {
              return Text(
                "${controller.count}",
                style: TextStyle(fontSize: 50),
              );
            },
          ),
          GetBuilder<CountControllerWithGetX>(
            id: "second",
            builder: (controller) {
              return Text(
                "${controller.count}",
                style: TextStyle(fontSize: 50),
              );
            },
          ),
          buildRaisedButton("first"),
          buildRaisedButton("second"),
        ],
      ),
    );
  }

  Widget buildRaisedButton(String id) {
    return RaisedButton(
      onPressed: () {
                // id를 넘겨줌
        _countControllerWithGetX.increase(id);
      },
      child: Text(
        "+",
        style: TextStyle(fontSize: 50),
      ),
    );
  }
}

참고

https://www.youtube.com/watch?v=k3hgQu6it4c 

 

728x90

GetX packages

https://pub.dev/packages/get

 

get | Flutter Package

Open screens/snackbars/dialogs without context, manage states and inject dependencies easily with GetX.

pub.dev

 

기본 페이지 라우팅

main.dart - GetMaterialApp 사용

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

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return GetMaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: Home(),
    );
  }
}

Page 이동

기존 라우팅

Navigator.of(context).push(MaterialPageRoute(builder: (_) => FirstPage()));

GetX 라우팅

Get.to(() => FirstPage());

뒤로 이동

기존 라우팅

Navigator.of(context).pop();

GetX 라우팅

Get.back();

페이지 이동시 이전 히스토리 삭제

기존 라우팅

Navigator.of(context).pushAndRemoveUntil(
                      MaterialPageRoute(builder: (_) => Home()),
                      (route) => false);

GetX 라우팅

Get.offAll(Home());

Named 페이징 라우팅

Named route 정의

main.dart

기존 라우팅

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return GetMaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      initialRoute: '/',
      routes: {
        '/': (context) => Home(),
        '/first': (context) => FirstNamedPage(),
        '/second': (context) => SecondNamedPage(),
      },
    );
  }
}

GetX 라우팅

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return GetMaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      initialRoute: '/',
      getPages: [
        GetPage(name: '/', page: () => Home()),
        GetPage(name: '/first', page: ()=>FirstNamedPage()),
        GetPage(name: '/second', page: ()=>SecondNamedPage()),
      ],
    );
  }
}

Page 이동

기존 라우팅

Navigator.of(context).pushNamed('/first');

GetX 라우팅

Get.toNamed('/first');

GetX 라우팅 현재페이지 삭제 후 이동

Get.offNamed('/second'); // /first페이지 삭제 후 /second 이동

페이지 이동시 이전 히스토리 삭제

기존 라우팅

Navigator.pushNamedAndRemoveUntil(context, '/', (route) => false);

GetX 라우팅

Get.offAllNamed('/');

페이지 전환 효과 적용

GetX

getPages의 transition 속성 지정

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return GetMaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      initialRoute: '/',
      getPages: [
        GetPage(name: '/', page: () => Home(), transition: Transition.zoom),
        GetPage(
            name: '/first',
            page: () => FirstNamedPage(),
            transition: Transition.zoom),
        GetPage(
            name: '/second',
            page: () => SecondNamedPage(),
            transition: Transition.zoom),
      ],
    );
  }
}

arguments 전달

GetX

// 전달할때
Get.toNamed('/first', arguments: "argument");
Get.to(FristPage(),arguments: 3);
Get.toNamed('/first', arguments: {"name":"name", "num":"11"});

// 받을때
Text("${Get.arguments}"),
Text("${Get.arguments.toString()}"),
Text("${Get.arguments['name']}"),

GetX 객체 전달

// 객체전달
class User {
  String name;
  int age;
  User(this.name, this.age);
}

Get.toNamed('/next', arguments: User("name", 11));

// 객체받기
Text("${(Get.arguments as User).name} : ${(Get.arguments as User).age} "),

동적 링크 사용

동적 링크 정의

GetPage(
  name: '/user/:uid', 
  page: () => UserPage(),
  transition: Transition.zoom)

파라미터 전달

Get.toNamed('/next/28353');

파라미터 받기

Text("${Get.parameters['uid']}"),

복잡한 파라미터 전달

Get.toNamed('/next/28353?name=name&age=11');

복잡한 파라미터 받기

Text("${Get.parameters['uid']}"),
Text("${Get.parameters['name']}"),
Text("${Get.parameters['age']}"),

참고

https://www.youtube.com/watch?v=OXfG-D4PNpQ 

 

728x90

2021.06.16 - [Usage/Flutter] - [Flutter] Bloc Pattern (stateful -> bloc -> skip event bloc )

 

[Flutter] Bloc Pattern (stateful -> bloc -> skip event bloc )

BLOC Bussiness Logic Component 비즈니스 로직과 ui를 구분 짓고 필요에 따라 원하는 부분만 데이터 처리 실 사용 예제 버튼 클릭시 증감 1. Stateful version ui widget - 버튼 클릭시 증감 //stateless widge..

mugon-devlog.tistory.com

Install package

https://pub.dev/packages/provider

Project 구조

main.dart
src
 - count_home.dart 
 - count_provider.dart
 - count_home_widget.dart // statelesswidget

main.dart

void main() {
  runApp(MyApp());
    // runApp에서 provider를 불러오면 MyApp에서 접근 가능
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
        visualDensity: VisualDensity.adaptivePlatformDensity,
      ),
      home: MultiProvider(
        providers: [
          ChangeNotifierProvider(
              create: (BuildContext context) => CounterProvider())
        ],
                // child의 모든 하위 요소들은 CounterProvider에 접근 가능
        child: Home(),
      ),
      // home: ChangeNotifierProvider(
      //   create: (BuildContext context) => CounterProvider(),
      //   child: Home(),
      // ),
    );
  }

count_provider.dart

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

// ChangeNotifier 상속
// bloc 에서 stream 처리, sink 주입 했던 것을 처리해줌
class CounterProvider extends ChangeNotifier {
    // private
    // 이곳에서 선언되는 모든 변수는 상태관리 대상
  int _count = 0;
    // get 함수를 사용해서 private한 변수에 접근 가능하도록 만듦
  int get count => _count;
  add() {
    _count++;
        // 상태값이 update 되었다는 신호를 보내줌
    notifyListeners();
  }

  subtract() {
    _count--;
    notifyListeners();
  }
}

count_home.dart

class Home extends StatelessWidget {
  const Home({Key key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
        // Provider.of로 사용할 provider를 가져옴
        // listen을 false로 해주면 현재의 widget은 rebuilding이 안됨
    CounterProvider countProvider =
        Provider.of<CounterProvider>(context, listen: false);
    return Scaffold(
      appBar: AppBar(
        title: Text("Provider"),
      ),
      body: CountView(),
      floatingActionButton: Row(
        mainAxisAlignment: MainAxisAlignment.end,
        children: [
          IconButton(
            icon: Icon(Icons.add),
            onPressed: () => countProvider.add(),
          ),
          IconButton(
            icon: Icon(Icons.remove),
            onPressed: () => countProvider.subtract(),
          ),
        ],
      ),
    );
  }

count_home_widget.dart

class CountView extends StatelessWidget {
  const CountView({Key key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Center(
      child: Text(
                // Provider.of를 통해 접근해서 count값 가져옴
                // build 전체를 rebuilding 함
        Provider.of<CounterProvider>(context).count.toString(),
      ),
    );
  }
}

// 특정 위젯만 rebuilding
class CountView extends StatelessWidget {
  const CountView({Key key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
     return Center(
            // consumer를 사용해서 특정 위젯만 rebuilding
       child: Consumer<CounterProvider>(
                builder: (context, provider, child) {
         return Text(
           provider.count.toString(),
         );
       }),
    );
  }
}

참고

https://www.youtube.com/watch?v=AmmjdvhQG1s 

 

728x90

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

[Flutter] Factory 패턴  (0) 2021.07.13
[Flutter] Stateful widget  (0) 2021.06.14
[UI초급] Container, materialApp, Scaffold  (0) 2021.06.07

+ Recent posts