import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
// This widget is the root of your application.
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key, required this.title});
// 상태 관리용 데이터가 아니다.
final String title;
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
// 상태 -
// 원래는 버튼 -> 로직이 실행돼서 -> 화면이 바뀐다.
// State는 버튼 -> 값이 바뀌어서 -> 화면이 바귄다.
// Test위젯 지우고 new Text위젯 넣기
// State는 버튼 -> 값이 바뀌어서 -> 화면이 바뀐다.
// Text 위젯에 State만 입력해두면
// 개발자가 지우고 새로 넣지 않아도
// 자동으로 갱신 된다.
int _counter = 0;
void _incrementCounter() {
setState(() {
_counter++;
});
}
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.headlineMedium,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: const Icon(Icons.add),
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
}
import 'dart:math';
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
// This widget is the root of your application.
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home:Parent(),
);
}
}
class Parent extends StatefulWidget {
const Parent({Key? key}) : super(key: key);
State<Parent> createState() => _ParentState();
}
class _ParentState extends State<Parent> {
// 2번 자식한테 줄 함수
int count = 0;
// 1번 자식한테 줄 함수
void setCount(int newCount){
setState(() {
count = newCount;
});
}
Widget build(BuildContext context) {
return Row(
children: [
Child1(callback: setCount),
Child2(count: count),
],
);
}
}
class Child1 extends StatelessWidget {
final Function callback;
const Child1({Key? key, required this.callback}) : super(key: key);
Widget build(BuildContext context) {
return ElevatedButton(
onPressed: (){
callback(Random().nextInt(100));
},
child: const Text("난수 생성"),
);
}
}
class Child2 extends StatelessWidget {
final int count;
const Child2({Key? key, required this.count}) : super(key: key);
Widget build(BuildContext context) {
return Text(count.toString());
}
}

Futer - 자스 promise 와 비슷
promise.then()
Iterable - 나열할 수 있는 것
list / set / map 등
<자바에서의 Stream>
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collector;
import java.util.stream.Collectors;
class Temp {
public static void main(String[] args) {
// Stream
List<String> list = List.of("jaybon", "sunny", "duck");
// 리스트에 u가 포함됨 문자열만 추출해서 리스트로 변환
List<String> result1 = new ArrayList<>();
for (String str : list) {
if (str.contains("u")) {
result1.add(str);
}
}
// 리스트 문자열에 @temp.com 을 붙여서 리스트로 반환
List<String> result2 = new ArrayList<>();
for (String str : result1) {
result2.add(str + "@temp.com");
}
// Stream 방식
// Stream 은 데이터의 흐름을 이용해서 작업을 한다.
// Java에서 Stream 은 한꺼번에 처리된다.
// .collect 등을 호출 했을 때
// 보기가 쉽다.
// 함수형 프로그래밍을 이용할 수 있다.
List<String> collect = list.stream()
.filter(str -> str.contains("u"))
.map(str -> str + "@temp.com")
.collect(Collectors.toList());
}
}
자바 Stream
- 자바 Collections를 가공할 때 사용.
- 한꺼번에 처리된다.
다트 Stream
- 비동기 데이터를 처리할 때 사용
- 언제 들어올 지 모를 데이터.
- 실시간 데이터가 들어올 때마다 처리된다.
<main.dart>
import 'package:flutter/material.dart';
import 'package:flutter_stream_counter/counter_controller.dart';
void main(){
runApp(CounterApp());
}
class CounterApp extends StatelessWidget {
const CounterApp({Key? key}) : super(key: key);
Widget build(BuildContext context) {
return MaterialApp(
home: CounterPage(),
);
}
}
class CounterPage extends StatelessWidget {
const CounterPage({Key? key}) : super(key: key);
Widget build(BuildContext context) {
final counterController = CounterController();
return StreamBuilder<int>(
stream: counterController.counterStream,
builder: (context, snapshot) {
return Scaffold(
floatingActionButton: FloatingActionButton(
onPressed: () {
var nowCount = snapshot.data?? 0;
counterController.addCount(nowCount + 1);
},
),
body: SafeArea(
child: Center(
child: Text(
"${snapshot.data ?? "0"}",
style: TextStyle(fontSize: 50),),
),
),
);
}
);
}
}
<counter_controller.dart>
// counter_controller.dart
import 'dart:async';
class CounterController{
final _counterStreamController = StreamController<int>();
Stream<int> get counterStream => _counterStreamController.stream;
void addCount(int newCount){
_counterStreamController.sink.add(newCount);
}
}

bloc/cubit
riverpod
getx - 상태관리/페이지이동/창띄우기/통신
provider - 전역 상태 관리(페이지 넘어갈시 데이터 보존)
: 위젯 간의 데이터 전달이 쉽다.
: 로그인 / 테마
hooks - 개별 위젯 상태 관리
: 리액트의 함수형 컴포넌트의 상태관리 기능이다.
: flutter_hooks는 리액트의 hooks를 따라 만들었다.
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
void main(){
runApp(CounterApp());
}
class CounterApp extends StatelessWidget {
const CounterApp({Key? key}) : super(key: key);
Widget build(BuildContext context) {
return MaterialApp (
home: CounterPage()
);
}
}
class CounterPage extends HookWidget {
const CounterPage({Key? key}) : super(key: key);
Widget build(BuildContext context) {
// hooks는 무조건 build 밑에 만들기
final counter = useState<int>(0);
return Scaffold(
floatingActionButton: FloatingActionButton(
onPressed: (){
counter.value++;
},
),
body: SafeArea(
child: Center(
child: Text(
"${counter.value}",
style: const TextStyle(fontSize: 50),
),
),
),
);
}
}

상태가 변하면 위젯의 build 함수가 실행된다.
use 시리즈는 데이터가 유지됨.
밑의 두가지는 잘쓰이지 않음.
useCallback - 함수를 저장
useMemoized - 상태를 가공해서 저장
<main.dart>
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:flutter_stateful_1/counter_controller.dart';
import 'package:provider/provider.dart';
void main() {
runApp(
MultiProvider(
providers: [
ChangeNotifierProvider(create: (context) => CounterController()),
],
child: const MyApp(),
),
);
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
// This widget is the root of your application.
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home:Parent(),
);
}
}
class Parent extends StatefulWidget {
const Parent({Key? key}) : super(key: key);
State<Parent> createState() => _ParentState();
}
class _ParentState extends State<Parent> {
// 2번 자식한테 줄 함수
int count = 0;
// 1번 자식한테 줄 함수
void setCount(int newCount){
setState(() {
count = newCount;
});
}
Widget build(BuildContext context) {
return Row(
children: [
Child1(callback: setCount),
Child2(count: count),
],
);
}
}
class Child1 extends StatelessWidget {
final Function callback;
const Child1({Key? key, required this.callback}) : super(key: key);
Widget build(BuildContext context) {
final counterController = context.read<CounterController>();
return ElevatedButton(
onPressed: (){
counterController.changeTo(Random().nextInt(100));
},
child: const Text("난수 생성"),
);
}
}
class Child2 extends StatelessWidget {
final int count;
const Child2({Key? key, required this.count}) : super(key: key);
Widget build(BuildContext context) {
final counterController = context.watch<CounterController>();
return Text("${counterController.count}");
}
}
<counter_controller.dart>
// counter_controller.dart
import 'package:flutter/cupertino.dart';
class CounterController with ChangeNotifier {
int _count = 0; // _ 표시는 private
int get count => _count;
void changeTo(int newCount){
_count = newCount;
// 상태 변경 후 무조건 호출
notifyListeners();
}
}
