Jin's Dev Story

[Flutter] Provider : 3-step 다른 방법 본문

Web & Android/Flutter

[Flutter] Provider : 3-step 다른 방법

woojin._. 2023. 9. 23. 14:25

3-step

Provider

  • 전송 없이 모든 위젯이 state를 직접 가져다 쓸 수 있게 만들어주는 패키지

패키지 설치

  1. pubspec.yaml에 코드 추가 후 pub get
  2. dependencies: flutter: sdk: flutter provider: ^6.0.1
  3. dart 파일에 import
  4. import 'package:provider/provider.dart';

Provider 사용법

  1. state 보관하는 store 필요
    1. class 생성해서 ChangeNotifier을 extends 하기
    class Store1 extends ChangeNotifier { 
      var name = 'john kim';  // state
    }
  2. store 등록 필요
    1. store를 사용할 위젯들을 전부 ChangeNotifierProvider()로 감싸기
    2. 모든 위젯이 사용할 경우 MaterialApp()을 감싸기
      1. ChangeNotifierProvider() 안에
        1. create: 안에는 아까 만들었던 store 넣으면 되고
        2. child: 안에는 store 적용할 위젯을 넣기
      void main() {
        runApp(
            ChangeNotifierProvider(
                create: (c) => Store1(),
                child: MaterialApp(
                    theme: style.theme,
                    home: MyApp()
                )
            )
        );
      }
  3. store에 있던 state 사용
    1. context.watch<store명>()
    Text(context.watch<Store1>().name)

state 변경

  • state 변경하는 함수를 만들고 그걸 부르는 식
// 이름 변경하는 함수
class Counter extends ChangeNotifier { 
  var name = 'john kim';
  changeName() {
    name = 'john park';
    notifyListeners();   // 재렌더링
  }
}

전체 코드

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import './style.dart' as style;
import 'package:http/http.dart' as http;
import 'dart:convert';
import 'package:flutter/rendering.dart';
import 'package:image_picker/image_picker.dart';
import 'dart:io';
import 'package:shared_preferences/shared_preferences.dart';
import 'dart:convert';
import 'package:provider/provider.dart';

void main() {
  runApp(
      ChangeNotifierProvider(
        create: (c) => Store1(),
        child: MaterialApp(
            theme: style.theme,
            home: MyApp()
        ),
      ));
}

class MyApp extends StatefulWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  var tab = 0;  // 1. 현재 상태 저장
  var list = [1, 2, 3];
  var map = {'name':'john', 'age':20};
  var data = [];
  var userImage; // 유저가 삽입한 이미지 저장 공간
  var userContent; // 유저가 입력한 글 저장 공간

  saveData() async {
    var storage = await SharedPreferences.getInstance();

    var map = {'age' : 20};
    storage.setString('map', jsonEncode(map));
    var result = storage.getString('map') ?? '업는데요';
    print(jsonDecode(result)['age']);
  }

  addMyData() {
    var myData = {
      'id': data.length,
      'image': userImage,
      'likes': 5,
      'date': 'July 25',
      'content': userContent,
      'liked': false,
      'user': 'John Kim'
    };
    setState(() {
      data.insert(0, myData);
    });
  }
  setUserContent(a) {
    setState(() {
      userContent = a;
    });
  }

  addData(a) {
    setState(() {
      data.add(a);
    });
  }

  getData() async {
    var result = await http.get(Uri.parse('https://codingapple1.github.io/app/data.json'));

    var result2 = jsonDecode(result.body);
    setState(() {
      data = result2;
    });

  }

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
          title: Text('Instagram'),
          actions: [
            IconButton(
              icon: Icon(Icons.add_box_outlined),
              onPressed: () async {
                var picker = ImagePicker();
                var image = await picker.pickImage(source: ImageSource.gallery);
                if(image != null)  {
                  setState(() {
                    userImage = File(image.path);
                  });
                }

                Navigator.push(context,
                    MaterialPageRoute(builder: (c) => Upload( userImage : userImage, setUserContent : setUserContent, addMyData : addMyData) )
                );
              },
              iconSize: 30,
            )
          ]),
      body: [Home(data : data, addData : addData), Text('샵페이지')][tab],  // 2. state에 따라 tab 보이는 상태 변경
      bottomNavigationBar: BottomNavigationBar(
        showSelectedLabels: false,
        showUnselectedLabels: false,
        onTap: (i){
          setState(() {
            tab = i;
          });
        },
        items: [
          BottomNavigationBarItem(icon: Icon(Icons.home_outlined), label: '홈'),
          BottomNavigationBarItem(icon: Icon(Icons.shopping_bag_outlined), label: '샵')
        ],
      ),
    );
  }
}

class Home extends StatefulWidget {
  const Home({Key? key, this.data, this.addData}) : super(key: key);
  final data, addData;

  @override
  State<Home> createState() => _HomeState();
}

class _HomeState extends State<Home> {

  var scroll = ScrollController();

  getMore() async {
    var result = await http.get(Uri.parse('https://codingapple1.github.io/app/more1.json'));
    var result2 = jsonDecode(result.body);

    widget.addData(result2);
  }

  @override
  void initState() {
    super.initState();
    scroll.addListener(() {   // 스크롤바 높이 측정 코드
      if(scroll.position.pixels == scroll.position.maxScrollExtent) {
        getMore();
      }
    });
  }

  @override
  Widget build(BuildContext context) {

    if (widget.data.isNotEmpty) {
      return ListView.builder(
          itemCount: widget.data.length,
          controller: scroll,
          itemBuilder: (c, i) {
            return Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                widget.data[i]['image'].runtimeType == String ? Image.network(widget.data[i]['image']) : Image.file(widget.data[i]['image']),
                Container(
                  constraints: BoxConstraints(maxWidth: 600),
                  padding: EdgeInsets.all(20),
                  width: double.infinity,
                  child: Column(
                    children: [
                      GestureDetector(
                        child: Text(widget.data[i]['user']),
                        onTap: () {
                          Navigator.push(context,
                              PageRouteBuilder(pageBuilder: (c, a1, a2) => Profile(),
                                  transitionsBuilder: (c, a1, a2, child) =>
                                      FadeTransition(opacity: a1, child: child)
                              )
                          );
                        },
                      ),
                      Text('좋아요 ${widget.data[i]['likes'].toString()}'),
                      Text(widget.data[i]['date']),
                      Text(widget.data[i]['content'])
                    ],
                  ),
                )
              ],
            );
          }
      );
    } else {
      return Text('로딩중임');
    }
  }
}

class Upload extends StatelessWidget {
  const Upload({Key? key, this.userImage, this.setUserContent, this.addMyData}) : super(key: key);
  final userImage, setUserContent, addMyData;

  @override

  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(actions: [
          IconButton(onPressed: () {
            addMyData();
          }, icon: Icon(Icons.send))
        ],),
        body: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            IconButton(
                onPressed: (){
                  Navigator.pop(context);
                },
                icon: Icon(Icons.close)
            ),
            Image.file(userImage),
            Text('이미지업로드화면'),
            TextField(onChanged: (text) {  // text는 유저가 입력한 글
              setUserContent(text);  // TextField()에 입력값 변할 때마다 onChanged 안의 함수가 실행됨
            },),
          ],
        )
    );
  }
}

class Store1 extends ChangeNotifier {  // state 보관함
  var name = 'john kim';
  var follower = 0;
  
  changeName() {
    name = 'john park';
    notifyListeners();  // 재렌더링
  }
}

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(title: Text(context.watch<Store1>().name),),
        body: Column(
          children: [
            ElevatedButton(onPressed: () {
              context.read<Store1>().changeName();
            }, child: Text('버튼'))
          ]
        )
    );
  }
}