Flutter Hooks, sometimes Jetpack Compose
Flutter1Flutter Hooksɺɹɹɹ࣌ʑ Jetpack ComposeDaichi Furiya@wasabeef_jp
View Slide
Target audienceHooks ΛΒͳ͍ਓ React Hook ͬͯΔਓ2͜ΕΛظʹ Hooks ʹ৮ΕͯɺFlutter Hooks/React Hook ͱJetpack Compose ͷεςʔτཧΛཧղͷҰาΛਐΉFlutter Hooks ͱ React Hookɺ΄΅ಉ͡ͳͷͰมͭͭ͠ Jetpack Compose ͷεςʔτཧʹ৮ΕΔJetpack Compose ͱ FlutterHooks/React Hooks ΛมͰ͖ΔΑ͏ʹ͢ΔJetpack Compose ͬͯΔਓ
Target audienceHooks ΛΒͳ͍ਓ React Hook ͬͯΔਓ3͜ΕΛظʹ Hooks ʹ৮ΕͯɺFlutter Hooks/React Hook ͱJetpack Compose ͷεςʔτཧΛཧղͷҰาΛਐΉFlutter Hooks ͱ React Hookɺ΄΅ಉ͡ͳͷͰมͭͭ͠ Jetpack Compose ͷεςʔτཧʹ৮ΕΔJetpack Compose ͱ FlutterHooks/React Hooks ΛมͰ͖ΔΑ͏ʹ͢ΔJetpack Compose ͬͯΔਓ
Target audienceHooks ΛΒͳ͍ਓ React Hook ͬͯΔਓ4͜ΕΛظʹ Hooks ʹ৮ΕͯɺFlutter Hooks/React Hook ͱJetpack Compose ͷεςʔτཧΛཧղͷҰาΛਐΉFlutter Hooks ͱ React Hookɺ΄΅ಉ͡ͳͷͰมͭͭ͠ Jetpack Compose ͷεςʔτཧʹ৮ΕΔJetpack Compose ͱ FlutterHooks/React Hooks ΛมͰ͖ΔΑ͏ʹ͢ΔJetpack Compose ͬͯΔਓ
Target audienceHooks ΛΒͳ͍ਓ React Hook ͬͯΔਓ5͜ΕΛظʹ Hooks ʹ৮ΕͯɺFlutter Hooks/React Hook ͱJetpack Compose ͷεςʔτཧΛཧղͷҰาΛਐΉFlutter Hooks ͱ React Hookɺ΄΅ಉ͡ͳͷͰมͭͭ͠ Jetpack Compose ͷεςʔτཧʹ৮ΕΔJetpack Compose ͱ FlutterHooks/React Hooks ΛมͰ͖ΔΑ͏ʹ͢ΔJetpack Compose ͬͯΔਓ
602 StatelessWidget, StatefulWidget01 Flutter Hooks03 useXXX04 Use Cases05 Conclusion
Flutter HooksFlutter Hooks ͱʁ ϝϦοτ7React Hooks ͷ Flutter ൛Ͱจ๏͍ํ΄΅ҰॹɻεςʔτͷॳظԽʢinitStateʣഁغ (dispose)ΛΟδΣοτͱ͕Ͱ͖Δɻ୯७ʹΫϥεͷఆٛΛݮΒ͢γϯλοΫεγϡΨʔతͳԸܙ͋Δɻ
Flutter HooksFlutter Hooks ͱʁ ϝϦοτ8React Hooks ͷ Flutter ൛Ͱจ๏͍ํ΄΅ҰॹɻεςʔτͷॳظԽʢinitStateʣഁغ (dispose)ΛΟδΣοτͱ͕Ͱ͖Δɻ୯७ʹΫϥεͷఆٛΛݮΒ͢γϯλοΫεγϡΨʔతͳԸܙ͋Δɻ
Flutter HooksFlutter Hooks ͱʁ ϝϦοτ9React Hooks ͷ Flutter ൛Ͱจ๏͍ํ΄΅ҰॹɻεςʔτͷॳظԽʢinitStateʣഁغ (dispose)ΛΟδΣοτͱ͕Ͱ͖Δɻ୯७ʹΫϥεͷఆٛΛݮΒ͢γϯλοΫεγϡΨʔతͳԸܙ͋Δɻ
1002 StatelessWidget, StatefulWidget01 Flutter Hooks03 useXXX04 Use Cases05 Conclusion
11StatelessWidget, StatefulWidgetStatelessWidget StatefulWidgetΦϒδΣΫτੜ࣌ʹ༩͑ΒΕͨใͱ BuildContext Ҏ֎ʹґଘ͠ͳ͍ΟδΣοτͰ͢ɻIconɺIconButton Text ͳͲ͕StatelessWidget ͷαϒΫϥεͰ͢ɻγεςϜͷঢ়ଶͳͲʹΑͬͯಈతʹมԽΛ͢Δখ͞ͳϥΠϑαΠΫϧΛͬͨΟδΣοτͰ͢ɻImageɺTextFormField Hero ͳͲ͕StatefulWidget ͷαϒΫϥεͰ͢ɻ
12StatelessWidget, StatefulWidgetStatelessWidget StatefulWidgetΦϒδΣΫτੜ࣌ʹ༩͑ΒΕͨใͱ BuildContext Ҏ֎ʹґଘ͠ͳ͍ΟδΣοτͰ͢ɻIconɺIconButton Text ͳͲ͕StatelessWidget ͷαϒΫϥεͰ͢ɻγεςϜͷঢ়ଶͳͲʹΑͬͯಈతʹมԽΛ͢Δখ͞ͳϥΠϑαΠΫϧΛͬͨΟδΣοτͰ͢ɻImageɺTextFormField Hero ͳͲ͕StatefulWidget ͷαϒΫϥεͰ͢ɻ
13StatelessWidget, StatefulWidgetStatelessWidget StatefulWidgetΦϒδΣΫτੜ࣌ʹ༩͑ΒΕͨใͱ BuildContext Ҏ֎ʹґଘ͠ͳ͍ΟδΣοτͰ͢ɻIconɺIconButton Text ͳͲ͕StatelessWidget ͷαϒΫϥεͰ͢ɻγεςϜͷঢ়ଶͳͲʹΑͬͯಈతʹมԽΛ͢Δখ͞ͳϥΠϑαΠΫϧΛͬͨΟδΣοτͰ͢ɻImageɺTextFormField Hero ͳͲ͕StatefulWidget ͷαϒΫϥεͰ͢ɻ
14StatelessWidget, StatefulWidgetclass Counter extends StatefulWidget {@overrideState createState() "=> _CountState();}class _CountState extends State {int count = 0;void setCount(int value) {setState(() "=> count = value);}@overrideWidget build(BuildContext context) {return TextButton(child: Text("a is $count"),onPressed: () "=> setCount(count + 1),);}}StatelessWidgetclass SimpleLabel extends StatelessWidget {const SimpleLabel({required this.label});final String label;@overrideWidget build(BuildContext context) {return Text(label,style: const TextStyle(decoration: TextDecoration.underline,),);}}StatefulWidgetϘλϯΛԡ͢ຖʹ count ͕૿͑ɺςΩετ͕Մม͢ΔΟδΣοτˢ↑ ༩͑ΒΕͨใͷΈͰෆมͳΟδΣοτ
15StatelessWidget, StatefulWidgetclass Counter extends StatefulWidget {@overrideState createState() "=> _CountState();}class _CountState extends State {int count = 0;void setCount(int value) {setState(() "=> count = value);}@overrideWidget build(BuildContext context) {return TextButton(child: Text("a is $count"),onPressed: () "=> setCount(count + 1),);}}StatelessWidgetclass SimpleLabel extends StatelessWidget {const SimpleLabel({required this.label});final String label;@overrideWidget build(BuildContext context) {return Text(label,style: const TextStyle(decoration: TextDecoration.underline,),);}}StatefulWidgetϘλϯΛԡ͢ຖʹ count ͕૿͑ɺςΩετ͕Մม͢ΔΟδΣοτˢ↑ ༩͑ΒΕͨใͷΈͰෆมͳΟδΣοτ
16StatelessWidget, StatefulWidgetclass Counter extends StatefulWidget {@overrideState createState() "=> _CountState();}class _CountState extends State {int count = 0;void setCount(int value) {setState(() "=> count = value);}@overrideWidget build(BuildContext context) {return TextButton(child: Text("a is $count"),onPressed: () "=> setCount(count + 1),);}}StatelessWidgetclass SimpleLabel extends StatelessWidget {const SimpleLabel({required this.label});final String label;@overrideWidget build(BuildContext context) {return Text(label,style: const TextStyle(decoration: TextDecoration.underline,),);}}StatefulWidgetϘλϯΛԡ͢ຖʹ count ͕૿͑ɺςΩετ͕Մม͢ΔΟδΣοτˢ↑ ༩͑ΒΕͨใͷΈͰෆมͳΟδΣοτ
17class CountPage extends StatefulWidget {@overrideState createState() "=> _CountState();}class _CountState extends State {int count = 0;@overridevoid initState() { ""/** ॳظԽ **/ }@overridevoid dispose() { ""/** ഁغ **/ }void setCount(int value) "=> setState(() "=> count = value);@overrideWidget build(BuildContext context) {return TextButton(child: Text("Count is $count"),onPressed: () "=> setCount(count + 1),);}}StatefulWidget Λ͏ʹجຊతʹೋͭͷΫϥεΛ࡞Δඞཁ͕͋Γ·͢ɻStatefulWidget Λܧঝͨ͠ CountPage ͱState Λܧঝͨ͠ _CountState Ͱ͢ɻinitState ͰॳظԽɺdispose ͰഁغͷॲཧΛॻ͘͜ͱ͕Ͱ͖·͢ɻ·ͨ setState ΛݺͿ͜ͱͰঢ়ଶ͕มԽͨ͜͠ͱΛ௨ͯ͠ϦϏϧυ͕Γ·͢ɻStatelessWidget, StatefulWidget
18class CountPage extends HookWidget {@overrideWidget build(BuildContext context) {final count = useState(0);return TextButton(child: Text("Count: ${count.value}"),onPressed: () "=> count.value"++,);}}StatelessWidget, StatefulWidgetγϯϓϧʹ useState Λ͏ͱॻ͘ͱ͜Ε͘Β͍Ͱલͷϖʔδͱಉ͡ίʔυͷಈ࡞Λ࣮ݱ͢Δ͜ͱ͕Ͱ͖·͢ɻuseState ͷׂͳͲޙड़͍͖ͯ͠·͢ɻ
19class HomePage extends HookWidget {const HomePage({Key? key}) : super(key: key);@overrideWidget build(BuildContext context) {final enabled = useState(false);return Column(children: [TextFormField(onChanged: (value) "=> enabled.value = value.isNotEmpty,),ElevatedButton(onPressed: enabled.value? () { ""/** Submit **/ }: null,child: Text('Submit'),),],);}}StatelessWidget, StatefulWidgetFlutter Hooks ͷॳΊͯͷαϯϓϧΛ୳ͯ͠ΈΔͱઌ΄ͲΑ͏ʹΧϯλʔͷίʔυΛΑ͘ݟ͔͚·͕͢ɺ࣮ࡍʹॳݟͰͦΕ͚ͩΈͯϐϯͱ͜ͳ͔ͬͨͷͰɺ͜͜ͰϑΥʔϜʹ͕͋Δ͔Ͳ͏͔ͰϘλϯͷ༗ޮແޮΛΓସ͍͑ͯ·͢ɻ
2002 StatelessWidget, StatefulWidget01 Flutter Hooks03 useXXX04 Use Cases05 Conclusion
Flutter Hooks ͷϝιουHookWidget Λܧঝͨ͠ build ͷதͰݺͳ͍ͱ͍͚ͳ͍useContextBuildContext ΛऔಘͰ͖Δ21
@Composablefun SampleName() {val context = LocalContext.currentText(text = "${context.getString(R.string.name)}")}22useContextclass SampleName extends HookWidget {@overrideWidget build(BuildContext context) {final l10n = useL10n();return Text(l10n.name);}}L10n useL10n() {final context = useContext();return L10n.of(context)!;}Flutter Hooks Jetpack ComposeHookWidget Λܧঝ͢Δඞཁ͕͋ΔɻHooks Λͬͨϝιουʹৗʹ use Ͱ࢝·Δ͜ͱ͕ϧʔϧͱͳ͍ͬͯΔɻ
@Composablefun SampleName() {val context = LocalContext.currentText(text = "${context.getString(R.string.name)}")}23useContextclass SampleName extends HookWidget {@overrideWidget build(BuildContext context) {final l10n = useL10n();return Text(l10n.name);}}L10n useL10n() {final context = useContext();return L10n.of(context)!;}Flutter Hooks Jetpack ComposeHookWidget Λܧঝ͢Δඞཁ͕͋ΔɻHooks Λͬͨϝιουʹৗʹ use Ͱ࢝·Δ͜ͱ͕ϧʔϧͱͳ͍ͬͯΔɻ
@Composablefun SampleName() {val context = LocalContext.currentText(text = "${context.getString(R.string.name)}")}24useContextclass SampleName extends HookWidget {@overrideWidget build(BuildContext context) {final l10n = useL10n();return Text(l10n.name);}}L10n useL10n() {final context = useContext();return L10n.of(context)!;}Flutter Hooks Jetpack ComposeHookWidget Λܧঝ͢Δඞཁ͕͋ΔɻHooks Λͬͨϝιουʹৗʹ use Ͱ࢝·Δ͜ͱ͕ϧʔϧͱͳ͍ͬͯΔɻ
StatelessWidget ͰԿ͔͠Βͷঢ়ଶཧ͕͍ͨ͠ͱࢥ͕ͬͨ࣌ useStateΛ͏໘Ͱ͢ʢViewModel ૬Ͱอ࣋͢Δ΄ͲͰͳ͍ͷͳͲʣuseStatefinal count = useState(0);count.value = 3;େମ֮͑Δͷ͜Ε͚ͩStatefulWidget ΛΘͳ͍ ؆୯25
useStatefinal count = useState(0);count.value = 3;େମ֮͑Δͷ͜Ε͚ͩStatefulWidget ΛΘͳ͍ ؆୯26StatelessWidget ͰԿ͔͠Βͷঢ়ଶཧ͕͍ͨ͠ͱࢥ͕ͬͨ࣌ useStateΛ͏໘Ͱ͢ʢViewModel ૬Ͱอ࣋͢Δ΄ͲͰͳ͍ͷͳͲʣ
useStatefinal count = useState(0);count.value = 3;େମ֮͑Δͷ͜Ε͚ͩStatefulWidget ΛΘͳ͍ ؆୯27StatelessWidget ͰԿ͔͠Βͷঢ়ଶཧ͕͍ͨ͠ͱࢥ͕ͬͨ࣌ useStateΛ͏໘Ͱ͢ʢViewModel ૬Ͱอ࣋͢Δ΄ͲͰͳ͍ͷͳͲʣ
@Composablefun Count() {val count = remember { mutableStateOf(0) }Button(onClick = { count.value"++ }) {Text("Count: ${count.value}")}}28useStateclass CountPage extends HookWidget {@overrideWidget build(BuildContext context) {final count = useState(0);return TextButton(onPressed: () { count.value"++; },child: Text("Count: ${count.value}"),);}}Flutter Hooks Jetpack Composeremember composable Λར༻ͯ͠ঢ়ଶͷอ࣋Λߦ͏͜ͱ͕Ͱ͖Δfinal state = useState(initialValue);state.value = newValue;͜Ε͚ͩ
@Composablefun Count() {val count = remember { mutableStateOf(0) }Button(onClick = { count.value"++ }) {Text("Count: ${count.value}")}}29useStateclass CountPage extends HookWidget {@overrideWidget build(BuildContext context) {final count = useState(0);return TextButton(onPressed: () { count.value"++; },child: Text("Count: ${count.value}"),);}}Flutter Hooks Jetpack Composeremember composable Λར༻ͯ͠ঢ়ଶͷอ࣋Λߦ͏͜ͱ͕Ͱ͖Δfinal state = useState(initialValue);state.value = newValue;͜Ε͚ͩ
@Composablefun Count() {val (count, setCount) =remember { mutableStateOf(0) }Button(onClick = { setCount(count + 1) }) {Text("Count: ${count}")}}30useStateclass CountPage extends HookWidget {@overrideWidget build(BuildContext context) {final count = useState(0);return TextButton(onPressed: () { count.value"++; },child: Text("Count: ${count.value}"),);}}Flutter Hooks Jetpack ComposeJetpack Compose value ͱ setter Λ࣋ͭ tupleΛੜ͢Δ͜ͱͰ͖·͢ʢReact Ά͍ʣfinal state = useState(initialValue);state.value = newValue;͜Ε͚ͩ
@Composablefun Count() {val (count, setCount) =remember { mutableStateOf(0) }Button(onClick = { setCount(count + 1) }) {Text("Count: ${count}")}}31useStateclass CountPage extends HookWidget {@overrideWidget build(BuildContext context) {final count = useState(0);return TextButton(onPressed: () { count.value"++; },child: Text("Count: ${count.value}"),);}}Flutter Hooks Jetpack Composeremember composable Λར༻ͯ͠ঢ়ଶͷอ࣋Λߦ͏͜ͱ͕Ͱ͖Δfinal state = useState(initialValue);state.value = newValue;͜Ε͚ͩ
؆୯ͳϥΠϑαΠΫϧॲཧ͕ඞཁͩͬͨ߹ʹ useEffect Λ͍·͢ɻΟδΣοτੜ࣌ʹॳظԽ͍ͨ͠߹ͳͲɻuseEffectॳظԽഁغͷॲཧ͕ඞཁͩͬͨΒ32
DisposableEffect(id) {val subscription = source.subscribe(id)onDispose {"// ഁغ͞ΕΔ࣌ʹ࣮ߦ(ActivityഁغͳͲ)subscription.unsubscribe(id)}}33useEffectuseEffect(() "=> {final subscription = source.subscribe(id);return () "=> {"// ഁغ͞ΕΔ࣌ʹ࣮ߦsubscription.unsubscribe(id);};}, [id]);Flutter Hooks Jetpack Compose
DisposableEffect(id) {val subscription = source.subscribe(id)onDispose {"// ഁغ͞ΕΔ࣌ʹ࣮ߦ(ActivityഁغͳͲ)subscription.unsubscribe(id)}}34useEffectuseEffect(() "=> {final subscription = source.subscribe(id);return () "=> {"// ഁغ͞ΕΔ࣌ʹ࣮ߦsubscription.unsubscribe(id);};}, [id]);Flutter Hooks Jetpack Compose
DisposableEffect(id) {val subscription = source.subscribe(id)onDispose {"// ഁغ͞ΕΔ࣌ʹ࣮ߦ(ActivityഁغͳͲ)subscription.unsubscribe(id)}}35useEffectuseEffect(() "=> {final subscription = source.subscribe(id);return () "=> {"// ഁغ͞ΕΔ࣌ʹ࣮ߦsubscription.unsubscribe(id);};}, [id]);Flutter Hooks Jetpack Compose
LaunchedEffect(id) {"// ID ͕มߋ͞ΕΔͨͼ}SideEffect {"// ຖճ}LaunchedEffect(Unit) {"// ࠷ॳͷҰճ}36useEffectuseEffect(() "=> {"// ID ͕มߋ͞ΕΔͨͼ}, [id]);useEffect(() "=> {"// ຖճ});useEffect(() "=> {"// ࠷ॳͷҰճ}, []);Flutter Hooks Jetpack Compose
LaunchedEffect(id) {"// ID ͕มߋ͞ΕΔͨͼ}SideEffect {"// ຖճ}LaunchedEffect(Unit) {"// ࠷ॳͷҰճ}37useEffectuseEffect(() "=> {"// ID ͕มߋ͞ΕΔͨͼ}, [id]);useEffect(() "=> {"// ຖճ});useEffect(() "=> {"// ࠷ॳͷҰճ}, []);Flutter Hooks Jetpack Compose
LaunchedEffect(id) {"// ID ͕มߋ͞ΕΔͨͼ}SideEffect {"// ຖճ}LaunchedEffect(Unit) {"// ࠷ॳͷҰճ}38useEffectuseEffect(() "=> {"// ID ͕มߋ͞ΕΔͨͼ}, [id]);useEffect(() "=> {"// ຖճ});useEffect(() "=> {"// ࠷ॳͷҰճ}, const []);Flutter Hooks Jetpack Compose
39useEffectFlutter Hooks@overrideWidget build(BuildContext context) {final enabled = useState(false);useEffect(() {print("ॳճͷΈ");}, const []);return Column(children: [TextFormField(onChanged: (value) "=> enabled.value = value.isNotEmpty,),ElevatedButton(onPressed: enabled.value? () { ""/** Submit **/ }: null,child: Text('Submit'),),],);}ςΩετ͕ೖྗ͞ΕͨΒϘλϯ͕༗ޮͱͳΔαϯϓϧͰ͕ͨ͠ɺ͜ͷ useEffectͷ͍ํΛͨ͠߹ʹ࣮ߦ͞ΕΔͷ࠷ॳͷҰճͱͳΓ·͢ɻ
State ͱ͍͍ͯͨ͠Θ͚Ͱͳ͘ຖճಉ͡σʔλΛऔಘ͢ΔͷͰͳ͘Ωϟογϡ͓͖͍ͯͨ͠߹ͳͲʹuseMemoizedΛΩϟογϡ͍ͨ͠߹40
val now = remember(key) {LocalDateTime.now()}41useMemoizedfinal now = useMemoized(() {return DateTime.now();}, [key]);Flutter Hooks Jetpack Composeremember ʹΩʔΛ͢͜ͱͰ͖ɺΩʔ͕ಉ͡Ͱ͋ΕͦͷॲཧΩϟογϡ͞Ε·͢useMemoized Λ͏͜ͱͰΩϟογϡ͕Ͱ͖ɺΩʔ͕ಉ͡Ͱ͋ΕͦͷॲཧΩϟογϡ͞Ε·͢
val now = remember(key) {LocalDateTime.now()}42useMemoizedfinal now = useMemoized(() {return DateTime.now();}, [key]);Flutter Hooks Jetpack Composeremember ʹΩʔΛ͢͜ͱͰ͖ɺΩʔ͕ಉ͡Ͱ͋ΕͦͷॲཧΩϟογϡ͞Ε·͢useMemoized Λ͏͜ͱͰΩϟογϡ͕Ͱ͖ɺΩʔ͕ಉ͡Ͱ͋ΕͦͷॲཧΩϟογϡ͞Ε·͢
val now = remember(key) {LocalDateTime.now()}43useMemoizedfinal now = useMemoized(() {return DateTime.now();}, [key]);Flutter Hooks Jetpack Composeremember ʹΩʔΛ͢͜ͱͰ͖ɺΩʔ͕ಉ͡Ͱ͋ΕͦͷॲཧΩϟογϡ͞Ε·͢useMemoized Λ͏͜ͱͰΩϟογϡ͕Ͱ͖ɺΩʔ͕ಉ͡Ͱ͋ΕͦͷॲཧΩϟογϡ͞Ε·͢
@Composablefun Greeting() {val (count, setCount) = remember { mutableStateOf(0) }val now = remember(count) { LocalDateTime.now() }Column {Text(text = "Click Time: $now")Button(onClick = { setCount(count + 1) }) {Text("Count: ${count}")}}}44useMemoized@overrideWidget build(BuildContext context) {final count = useState(0);final now = useMemoized(() "=> DateTime.now(), [count.value]);return Column(children: [Text("Click Time: $now"),TextButton(onPressed: () "=> count.value"++,child: Text("Count: ${count.value}"),),],);}Flutter Hooks Jetpack Compose͜ͷྫͰɺcount ͕૿͑ͨʢΫϦοΫͨ͠ʣλΠϛϯάͰݱࡏ࣌ؒΛऔಘͯ͠Ωϟογϡ͍ͯ͠·͢
@Composablefun Greeting() {val (count, setCount) = remember { mutableStateOf(0) }val now = remember(count) { LocalDateTime.now() }Column {Text(text = "Click Time: $now")Button(onClick = { setCount(count + 1) }) {Text("Count: ${count}")}}}45useMemoized@overrideWidget build(BuildContext context) {final count = useState(0);final now = useMemoized(() "=> DateTime.now(), [count.value]);return Column(children: [Text("Click Time: $now"),TextButton(onPressed: () "=> count.value"++,child: Text("Count: ${count.value}"),),],);}Flutter Hooks Jetpack Compose͜ͷྫͰɺcount ͕૿͑ͨʢΫϦοΫͨ͠ʣλΠϛϯάͰݱࡏ࣌ؒΛऔಘͯ͠Ωϟογϡ͍ͯ͠·͢
@Composablefun Greeting() {val (count, setCount) = remember { mutableStateOf(0) }val now = remember(count) { LocalDateTime.now() }Column {Text(text = "Click Time: $now")Button(onClick = { setCount(count + 1) }) {Text("Count: ${count}")}}}46useMemoized@overrideWidget build(BuildContext context) {final count = useState(0);final now = useMemoized(() "=> DateTime.now(), [count.value]);return Column(children: [Text("Click Time: $now"),TextButton(onPressed: () "=> count.value"++,child: Text("Count: ${count.value}"),),],);}Flutter Hooks Jetpack Compose͜ͷྫͰɺcount ͕૿͑ͨʢΫϦοΫͨ͠ʣλΠϛϯάͰݱࡏ࣌ؒΛऔಘͯ͠Ωϟογϡ͍ͯ͠·͢
4702 StatelessWidget, StatefulWidget01 Flutter Hooks03 useXXX04 Use Cases05 Conclusion
48Use CasesVideoPlayerController useAssetVideoController({required String asset,bool autoPlay = false,bool looping = false,}) {final controller = useMemoized(() "=> VideoPlayerController.asset(asset),[asset],);useEffect(() {controller"..initialize()"..setLooping(looping);if (autoPlay) {controller.play();}return () "=> controller.dispose();}, [asset, autoPlay, looping]);return controller;}Flutter Hooksclass VideoPage extends HookWidget {@overrideWidget build(BuildContext context) {final videoController = useAssetVideoController(asset: Assets.videos.bigbuckbunny,autoPlay: true,looping: true,);return Scaffold(appBar: AppBar(),body: Center(child: AspectRatio(aspectRatio: 16 / 9,child: VideoPlayer(videoController),),),);}}video_player ΛͬͨྫɻVideoPlayerController ͷॳظԽɾഁغΛΟδΣοτͱ͓ͯ͘͜͠ͱͰɺผͷΟδΣοτͰ͍ճ͕͠Ͱ͖Δ
49Use Cases @Composablepublic fun BackHandler(enabled: Boolean = true, onBack: () "-> Unit) {val currentOnBack by rememberUpdatedState(onBack)val backCallback = remember {object : OnBackPressedCallback(enabled) {override fun handleOnBackPressed() {currentOnBack()}}}SideEffect {backCallback.isEnabled = enabled}val backDispatcher = checkNotNull(LocalOnBackPressedDispatcherOwner.current) {"No OnBackPressedDispatcherOwner"}.onBackPressedDispatcherval lifecycleOwner = LocalLifecycleOwner.currentDisposableEffect(lifecycleOwner, backDispatcher) {backDispatcher.addCallback(lifecycleOwner, backCallback)onDispose { backCallback.remove() }}}Jetpack Composeclass MainActivity : ComponentActivity() {override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContent {MaterialTheme {Surface() {BackHandler(onBack = {"// Back pressed})}}}}}Back key ͷϋϯυϦϯάΛ Composable Խͨ͠ྫɻ͜Εଞͷը໘Ͱ͍ճ͕͠Ͱ͖ΔΑ͏ʹͳΔ
5002 StatelessWidget, StatefulWidget01 Flutter Hooks03 useXXX04 Use Cases05 Conclusion
Thank you!https://wasabeef.jphttp://github.com/wasabeefhttps://twitter.com/wasabeef_jpResources51Daichi Furiya@wasabeef_jp