๐Ÿ’™Flutter ์œ„์ ฏ

์žฅ์ฑ„๋ฏผยท2024๋…„ 2์›” 12์ผ

ํŒ์—…์ฐฝ

  • showDialog

showDialog๋ฅผ ์ด์šฉํ•ด AlertDialog๋ฅผ ์‹คํ–‰์‹œํ‚ค๊ณ  content์•ˆ์— ๋‚ด์šฉ์„ ๋„ฃ์„ ์ˆ˜ ์žˆ๋‹ค.
AlertDialog

alertDialog

title : ์ œ๋ชฉ ์˜์—ญ
content : ๋‚ด์šฉ ์˜์—ญ
SingleChildScrollView, ListBody๋ฅผ ์ด์šฉํ•˜๋ฉด ์Šคํฌ๋กค ๋™์ž‘ ๊ฐ€๋Šฅ
action ํ”„๋กœํผํ‹ฐ์—๋Š” ๋ฒ„ํŠผ ์ ์šฉ
Navigator.of(context).pop() : ๋‹ค์ด์–ผ๋กœ๊ทธ ๋‹ซ๊ธฐ

class _MyHomePageState extends State<MyHomePage> {

void FlutterDialog() {
	showDialog(
       context: context,
       barrierDismissible: false, // ๋ฐ”๊นฅ ์˜์—ญ ํ„ฐ์น˜์‹œ ๋‹ซ์„์ง€ ์—ฌ๋ถ€
       builder: (BuildContext context) {
          return AlertDialog(
             title: Text('ํŒ์—… ๋ฉ”์‹œ์ง€'),
             content: SingleChildScrollView(
             child: ListBody(
                children: <Widget>[
                    Text('Alert Dialog ํ…Œ์ŠคํŠธ'),
                    Text('ok ๋ฒ„ํŠผ ํด๋ฆญํ•˜์„ธ์š”'),
                ],
             ),
          ),
       actions: <Widget>[
           FlatButton(
               child: Text('ok'),
               onPressed: () {
                  Navigator.of(context).pop();
               },
           ),
           FlatButton(
               child: Text('cancel'),
               onPressed: () {
                   Navigator.of(context).pop();
               },
            ),
         ],
      );
    }
  );
}

  
  Widget build(BuildContext context) {

    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Column(
        children: <Widget>[
          RaisedButton(
            child: Text('Alert Dialog test'),
            onPressed: () => FlutterDialog(),
          )
        ],
      )
    );
  }
}

showDialog(
        context: context,
        builder: (context) => StatefulBuilder(builder: (context, setState) {
//Dialog์•ˆ์— setState๋ฅผ ์ ์šฉํ•˜๋ ค๋ฉด StateFulbuilder๋ฅผ ๊ผญ ์ ์šฉํ•ด์•ผ ํ•œ๋‹ค.

              return AlertDialog(
                 shape: RoundedRectangleBorder(
                 //ํŒ์—…์ฐฝ์— radius๋ฅผ ์ฃผ๊ธฐ์œ„ํ•œ ์˜ต์…˜
                     borderRadius: BorderRadius.all(Radius.circular(32.0))),
                contentPadding: EdgeInsets.only(top: 0),
                //default ํŒจ๋”ฉ๊ฐ’์„ ์—†์•จ ์ˆ˜ ์žˆ๋‹ค.
                content:Container()

TextFormField

https://blog.codefactory.ai/flutter/form/

PopupMenuButton

SQlite

  • SQFlite๋Š” SQlite๋ฅผ ํ”Œ๋Ÿฌํ„ฐ์—์„œ๋„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ์ง€์›ํ•˜๋Š” ํ”Œ๋Ÿฌ๊ทธ์ธ
  • ๊ด€๊ณ„ํ˜• ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋กœ ํŒŒ์ผ ๊ธฐ๋ฐ˜์œผ๋กœ ํ•˜๋ฉฐ ์„œ๋ฒ„๊ฐ€ ์•„๋‹Œ ๋กœ์ปฌ ๊ธฐ๊ธฐ์— ์ €์žฅ๋œ๋‹ค

https://hanab.kr/%ed%94%8c%eb%9f%ac%ed%84%b0-flutter-sqlite-%ec%82%ac%ec%9a%a9%eb%b0%a9%eb%b2%95-%ec%98%88%ec%a0%9c/

์œ„์ ฏ ์ •๋ ฌ

  • ํ–‰(Row)์˜ ๊ฒฝ์šฐ, ์ฃผ์ถ•์˜ ๊ธฐ์ค€์€ ๊ฐ€๋กœ.
  • ์—ด(Column)์˜ ๊ฒฝ์šฐ, ์ฃผ์ถ•์˜ ๊ธฐ์ค€์€ ์„ธ๋กœ.

Transformation ์œ„์ ฏ

Transform.scale(
	scale:  4.2 ,    //์›๋ž˜ ์•„์ด์ฝ˜ ์‚ฌ์ด์ฆˆ์˜ 4.2๋ฐฐ๋กœ ํ‚ค์šฐ๊ธฐ
    child: icon(icons.add),   //์‚ฌ์ด์ฆˆ ๋ณ€๊ฒฝํ•  ์œ„์ ฏ ์ƒ์„ฑ
) //์‚ฌ์ด์ฆˆ ์กฐ์ ˆ(container๋ณด๋‹ค ํฌ๊ฒŒ ์„ค์ •ํ•ด๋„ container์˜ ํฌ๊ธฐ๋Š” ๋ณ€ํ•˜์ง€ ์•Š์Œ)

Transform.translate(
	offset: Offset(dx,dy),   //x์ถ•,y์ถ•์œผ๋กœ์˜ ์ด๋™๊ฑฐ๋ฆฌ ์„ค์ •
    child: Text('Hello'),    //offset๋งŒํผ ์ด๋™์‹œํ‚ฌ ์œ„์ ฏ ์ƒ์„ฑ 
)  //์œ„์น˜ ์กฐ์ ˆ

Transform.rotate(
	angle: pi/4 ,   //45deg
    child:      ,   //๊ฐ๋„ ๋ฐ”๊ฟ€ ์œ„์ ฏ ์ƒ์„ฑ
)  //๊ฐ๋„ ์กฐ์ ˆ

Container ์œ„์ ฏ

clipBehavior: Clip.hardEdge //css์˜ overflow์™€ ๋น„์Šทํ•œ ์†์„ฑ

reusable ์œ„์ ฏ ์ƒ์„ฑ

  1. ์œ„์ ฏ ํŒŒ์ผ ์ƒ์„ฑ
  2. class ์ƒ์„ฑ
  3. ๋ณ€๊ฒฝ๋˜๋Š” ์š”์†Œ๋“ค ๋ณ€์ˆ˜๋กœ ์„ ์–ธํ•ด์ฃผ๊ธฐ (final ๋ณ€์ˆ˜ํƒ€์ž… ๋ณ€์ˆ˜๋ช…;)
  4. constructor ์ƒ์„ฑ์ž ๋งŒ๋“ค๊ธฐ (code action์œผ๋กœ ์ž๋™ ์ƒ์„ฑ ๊ฐ€๋Šฅ)
    *code action ๋‹จ์ถ•ํ‚ค: ctrl+.
  5. ๋ณ€๊ฒฝ๋˜๋Š” ์š”์†Œ๋“ค ๋ณ€์ˆ˜๋ช…์œผ๋กœ ๋ฐ”๊ฟ”์ฃผ๊ธฐ
  6. const ์ œ๊ฑฐ

ํ•จ์ˆ˜ ์ƒ์„ฑ

  • ๋ฆฌํ„ดํƒ€์ž… ํ•จ์ˆ˜๋ช… (ํŒŒ๋ผ๋ฏธํ„ฐ) { }

SingleChildScrollView ์œ„์ ฏ

  • ์Šคํฌ๋กค ๊ฐ€๋Šฅํ•˜๊ฒŒ ํ•จ
  • ์œ„์ ฏ์ด ํ™”๋ฉด๋ณด๋‹ค overflow๋˜์„œ ์˜ค๋ฅ˜๋œฐ ๋•Œ ์ „์ฒด body๋ฅผ ๊ฐ์‹ธ๋ฉด ํ™”๋ฉด ์Šคํฌ๋กค์ด ๊ฐ€๋Šฅํ•ด์ง€๋ฉด์„œ ์˜ค๋ฅ˜๊ฐ€ ์‚ฌ๋ผ์ง

theme

theme: ThemeData(
	textTheme: const TextTheme(
    	titleLarge: TextStyle(
        	color: Colors.red,
        ),
    ),
),

Text('Hello, I am title',
	style: TextStyle(
    	color: Theme.of(context).textTheme.titleLarge!.color,  
    ),   //context๋Š” ์ด์ „์— ์žˆ๋Š” ๋ชจ๋“  ์ƒ์œ„ ์š”์†Œ๋“ค์— ๋Œ€ํ•œ ์ •๋ณด์ด๋‹ค
),   //dart๋Š” null safe์–ธ์–ด์ด๊ธฐ์— titleLarge๊ฐ€ null์ด ์•„๋‹˜์„ ์•Œ๋ ค์ค˜์•ผ ํ•œ๋‹ค.
     //?(๋ฌผ์Œํ‘œ)๋ฅผ ์‚ฌ์šฉํ•ด์„œ null์ด ์•„๋‹Œ ๊ฒฝ์šฐ์—๋งŒ ์‹คํ–‰ํ•˜๋ผ๊ณ  ์•Œ๋ ค์ค˜๋„ ๋œ๋‹ค.

Widget Life Cycle

  1. initState ์œ„์ ฏ
  • ์ƒํƒœ๋ฅผ ์ดˆ๊ธฐํ™”ํ•˜๊ธฐ ์œ„ํ•œ ๋ฉ”์†Œ๋“œ
  • ๋Œ€๋ถ€๋ถ„์˜ ์ƒํ™ฉ์—์„œ ํ•„์š”ํ•˜์ง€ ์•Š์Œ (๋ณ€์ˆ˜ ์ดˆ๊ธฐํ™”๋กœ๋„ ๊ฐ€๋Šฅํ•˜๊ธฐ ๋•Œ๋ฌธ)
  • build ๋นŒ๋“œ ๋ฉ”์†Œ๋“œ๋ณด๋‹ค ๋จผ์ € ํ˜ธ์ถœ๋˜์–ด์•ผ ํ•œ๋‹ค
  • initState๋ฉ”์„œ๋“œ๋Š” ๋‹จ ํ•œ ๋ฒˆ๋งŒ ํ˜ธ์ถœ๋œ๋‹ค
  • ๋ฐ˜๋“œ์‹œ super.initState()๋ฅผ ํ˜ธ์ถœํ•ด์•ผํ•œ๋‹ค

  1. didChangeDependencies()
  • initState() ๋‹ค์Œ์œผ๋กœ ํ˜ธ์ถœ๋˜๋ฉฐ, ์ดํ›„์—๋Š” build()๊ฐ€ ํ˜ธ์ถœ๋œ๋‹ค
  • inheritedWidget์ด๋‚˜ Provider๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ, InheritedWidget์ด๋‚˜ Provider์˜ ๋‚ด์šฉ์ด ๋ณ€๊ฒฝ๋  ๋•Œ๋งˆ๋‹ค ํ˜ธ์ถœ๋œ๋‹ค
  1. dispose ์œ„์ ฏ
  • ์œ„์ ฏ์„ ์œ„์ ฏํŠธ๋ฆฌ์—์„œ ์ œ๊ฑฐํ•  ๋•Œ ์‚ฌ์šฉ๋˜๋Š” ๋ฉ”์†Œ๋“œ

async

  • ์ž‘์—…์ด ์˜ค๋ž˜๊ฑธ๋ฆฌ๋Š” ๊ฒฝ์šฐ, ํ•ด๋‹น ์ž‘์—…์ด ๋๋‚  ๋•Œ๊นŒ์ง€ ๊ธฐ๋‹ค๋ฆฌ๊ฒŒ ํ•จ.
    (API์— ์š”์ฒญ์„ ํ•˜๊ณ  ๋ฐ”๋กœ ๋‹ค์Œ ์ฝ”๋“œ ์ž‘์—…์œผ๋กœ ๋„˜์–ด๊ฐ€๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ, ์„œ๋ฒ„๊ฐ€ ์‘๋‹ตํ•  ๋•Œ๊นŒ์ง€ ํ”„๋กœ๊ทธ๋žจ์„ ๊ธฐ๋‹ค๋ฆฌ๊ฒŒ ํ•˜๋Š” ๊ฒฝ์šฐ)

  • ๋ฆฌํ„ดํƒ€์ž… ํ•จ์ˆ˜๋ช… () async {}

- await

  • ์ž‘์—…์ด ์™„๋ฃŒ๋  ๋•Œ๊นŒ์ง€ ๊ธฐ๋‹ค๋ ค์•ผํ•˜๋Š” ์ฝ”๋“œ ์•ž์ชฝ์— ๋ถ™์—ฌ์ค€๋‹ค.
  • await์€ asynchronous function(๋น„๋™๊ธฐ ํ•จ์ˆ˜)๋‚ด์—์„œ๋งŒ ์‚ฌ์šฉ๋  ์ˆ˜ ์žˆ๋‹ค.
  • Future ํƒ€์ž…๊ณผ ์ฃผ๋กœ ์‚ฌ์šฉ๋จ
class ApiService {
	final String baseUrl="https://webtoon-crawler.nomadcoders.workers.dev";
	final String today="today";
    
void getTodaysToons() async {  //๋น„๋™๊ธฐํ•จ์ˆ˜ async
	final url=Uri.parse('$baseUrl/$today');
    final response=await http.get(url);  //getํ•จ์ˆ˜๋Š” httpํŒจํ‚ค์ง€์— ์žˆ๋Š” ํ•จ์ˆ˜์ด๋‹ค.  
    //await์„ ํ†ตํ•ด ์„œ๋ฒ„๊ฐ€ ์‘๋‹ตํ•  ๋•Œ๊นŒ์ง€ ๊ธฐ๋‹ค๋ฆฌ๊ณ  ์‘๋‹ตํ•˜๋ฉด response๋ผ๋Š” ๋ณ€์ˆ˜์—๋‹ค ๋ฐ์ดํ„ฐ๋ฅผ ์ €์žฅํ•œ๋‹ค.
    if(response.statusCode==200){  //์š”์ฒญ์— ์„ฑ๊ณตํ•˜๋ฉด
    	print(response.body);  //์„œ๋ฒ„๊ฐ€ ๋ณด๋‚ธ ๋ฐ์ดํ„ฐ ์ถœ๋ ฅํ•˜๊ธฐ
        return;
    }
    throw Error();
}

- jsonDecode

  • JSON ์ฝ”๋“œ๋กœ ๋ฐ”๊ฟ”์ฃผ๋Š” ํ•จ์ˆ˜
  • jsonDecond(String)
  • ๋ฐ˜ํ™˜๊ฐ’ ํƒ€์ž…์€ dynamic
final List<dynamic> webtoons=jsonDecode(response.body);

โžก๏ธapi ๋ถˆ๋Ÿฌ์˜ค๊ธฐ

class ApiService {
	final String baseUrl="https://webtoon-crawler.nomadcoders.workers.dev";
	final String today="today";
    
    Future<List<WebtoonModel>> getTodaysToons() async {  
    	//Future์€ ๋‹น์žฅ ์™„๋ฃŒ๋  ์ˆ˜ ์žˆ๋Š” ์ž‘์—…์ด ์•„๋‹˜์„ ๋œปํ•จ
        //๋น„๋™๊ธฐํ•จ์ˆ˜ async (์ž‘์—…์ด ์˜ค๋ž˜๊ฑธ๋ฆฌ๋Š” ๊ฒฝ์šฐ, ํ•ด๋‹น ์ž‘์—…์ด ๋๋‚  ๋•Œ๊นŒ์ง€ ๊ธฐ๋‹ค๋ฆฌ๊ฒŒ ํ•จ)
    	List<WebtoonModel> webtoonInstances=[];
        final url=Uri.parse('$baseUrl/$today');
        //ํ˜•๋ณ€ํ™˜ ๋ฉ”์†Œ๋“œ parse, toString, toList
        final response=await http.get(url);  
        //url์€ Uriํƒ€์ž…์ด์–ด์•ผ ํ•œ๋‹ค
        //getํ•จ์ˆ˜๋Š” httpํŒจํ‚ค์ง€์— ์žˆ๋Š” ํ•จ์ˆ˜
        //Futureํƒ€์ž…์„ ๋ฐ˜ํ™˜ํ•˜๋Š” ํ•จ์ˆ˜
        //await์„ ํ†ตํ•ด ์„œ๋ฒ„๊ฐ€ ์‘๋‹ตํ•  ๋•Œ๊นŒ์ง€ ๊ธฐ๋‹ค๋ฆฌ๊ณ  ์‘๋‹ตํ•˜๋ฉด response๋ผ๋Š” ๋ณ€์ˆ˜์—๋‹ค ๋ฐ์ดํ„ฐ๋ฅผ ์ €์žฅํ•œ๋‹ค.
        if(response.statusCode==200){  //http ์‘๋‹ต์€ ์ƒํƒœ์ฝ”๋“œ๋กœ ํ‘œํ˜„๋จ
        //(200์€ ํด๋ผ์ด์–ธํŠธ์˜ ์š”์ฒญ์„ ์ •์ƒ์ ์œผ๋กœ ์ˆ˜ํ–‰ํ–ˆ์„ ๋•Œ์˜ ์ƒํƒœ ์ฝ”๋“œ)
            final List<dynamic> webtoons=jsonDecode(response.body);
            for (var webtoon in webtoons){
                webtoonInstances.add(WebtoonModel.fromJson(webtoon));
            }
            return webtoonInstances;
        }
        throw Error();
    }
}


class WebtoonModel {
	final String title, thumb, id;
    
    WebtoonModel.fromJson(Map<String, dynamic> json)
    	//Map์€ ํ‚ค(Key)์™€ ๊ฐ’(Value)๋กœ ๊ตฌ์„ฑ๋œ ํด๋ž˜์Šค
        : title=json['title'],
          thumb=json['thumb'],
          id=json['id'];
    //named Constructor
    //์ƒ์„ฑ์ž ๋‚ด๋ถ€์—์„œ ํด๋ž˜์Šค์˜ ๋ฉค๋ฒ„ ๋ณ€์ˆ˜๋ฅผ ์ดˆ๊ธฐํ™”ํ•˜๋Š” ํŠน๋ณ„ํ•œ ๊ตฌ๋ฌธ์œผ๋กœ ์ƒ์„ฑ์ž์˜ ์ค‘๊ด„ํ˜ธ({}) ์ดํ›„์— ์ž‘์„ฑ๋œ๋‹ค.
    //Dart์—์„œ๋Š” json์„ ๋‹ค๋ฃจ๊ธฐ ์œ„ํ•ด Map<String, dynamic> ์ด๋ผ๋Š” ์ž๋ฃŒํ˜•์„ ์‚ฌ์šฉํ•œ๋‹ค. 
    //json ๋ฐ์ดํ„ฐ๋Š” ํ‚ค์™€ ๊ฐ’์œผ๋กœ ์ด๋ฃจ์–ด์ ธ์žˆ๊ณ  ํ‚ค๋Š” ๋ฐ˜๋“œ์‹œ String์ด๋ฉฐ ๊ฐ’์€ ์ž๋ฃŒํ˜•์ด ์ •ํ•ด์ ธ์žˆ์ง€ ์•Š๋‹ค. 
    //๋•Œ๋ฌธ์— Map<String, dynamic> ์ž๋ฃŒํ˜•์œผ๋กœ ๋ชจ๋“  json ๋ฐ์ดํ„ฐ๋ฅผ ๋‹ค๋ฃฐ ์ˆ˜ ์žˆ๋‹ค. (json ์ž๋ฃŒํ˜•)

์ƒํƒœ์ฝ”๋“œ

200 ํด๋ผ์ด์–ธํŠธ์˜ ์š”์ฒญ์„ ์ •์ƒ์ ์œผ๋กœ ์ˆ˜ํ–‰ํ•จ
201 ํด๋ผ์ด์–ธํŠธ๊ฐ€ ์–ด๋– ํ•œ ๋ฆฌ์†Œ์Šค ์ƒ์„ฑ์„ ์š”์ฒญ, ํ•ด๋‹น ๋ฆฌ์†Œ์Šค๊ฐ€ ์„ฑ๊ณต์ ์œผ๋กœ ์ƒ์„ฑ๋จ์„ ์˜๋ฏธ
400 ํด๋ผ์ด์–ธํŠธ์˜ ์š”์ฒญ์ด ๋ถ€์ ์ ˆํ• ๋•Œ
401 ํด๋ผ์ด์–ธํŠธ๊ฐ€ ์ธ์ฆ๋˜์ง€ ์•Š์€ ์ƒํƒœ์—์„œ ๋ฆฌ์†Œ์Šค๋ฅผ ์š”์ฒญํ• ๋•Œ
403 ์œ ์ €์ธ์ฆ์ƒํƒœ์™€ ๊ด€๊ณ„์—†์ด ์‘๋‹ตํ•˜๊ณ  ์‹ถ์ง€ ์•Š์€ ๋ฆฌ์†Œ์Šค๋ฅผ ์š”์ฒญํ• ๋•Œ
405 ํด๋ผ์ด์–ธํŠธ๊ฐ€ ์‚ฌ์šฉ๋ถˆ๊ฐ€๋Šฅํ•œ ๋ฆฌ์†Œ์Šค๋ฅผ ์š”์ฒญํ• ๋•Œ(ex.๊ณ„์ •, ์ธ์ฆ๊ด€๋ จ)
301 ํด๋ผ์ด์–ธํŠธ๊ฐ€ ์š”์ฒญํ•œ ๋ฆฌ์†Œ์Šค์— ๋Œ€ํ•œ URI๊ฐ€ ๋ณ€๊ฒฝ๋˜์—ˆ์„๋•Œ
500 ์„œ๋ฒ„์— ๋ฌธ์ œ๊ฐ€ ์žˆ์„๊ฒฝ์šฐ

๋‹จ์ถ•ํ‚ค

https://andaeng.tistory.com/46

0๊ฐœ์˜ ๋Œ“๊ธ€