Skip to content

FlutterでAndroidのFragment Transaction的な

親の顔より見たアレを Flutter でやります.

override fun onCreate(savedInstanceState: Bundle?) {
val fragment = MainFragment.newInstance()
supportFragmentManager.beginTransaction()
.replace(R.id.content, fragment)
.addToBackStack(null).commit()
supportFragmentManager.addOnBackStackChangedListener {
if (supportFragmentManager.backStackEntryCount > 0) {
supportActionBar?.setDisplayHomeAsUpEnabled(true)
} else {
supportActionBar?.setDisplayHomeAsUpEnabled(false)
}
}

state_notifier を使ってやっていきます.

切り替える Fragment 相当の View の種類の Enum

enum ViewType {
Main,
Sub,
}

StateNotifier を用意します.

@freezed
abstract class AppViewState with _$AppViewState {
const factory AppViewState({
@Default(ViewType.Main) viewType,
}) = _AppViewState;
}
class AppViewStateNotifier extends StateNotifier<AppViewState> {
AppViewStateNotifier() : super(const AppViewState()) {}
void setViewType(ViewType viewType) {
state = state.copyWith(viewType: viewType);
}
}

以下が Fragament 相当の View を持つ View です. WillPopScope は Activity でいう onBackPressed を実現する View だと思ってください. onWillPop で現在の View が Sub だった場合 MainView に切り替えするようにします. AppBar#leading でも View が Sub だったときは back_arrow をセットするようにして setDisplayHomeAsUpEnabled(true) 相当になるようにします. 今回は View が 2 つなのでこんな感じですが,View が 3 つとかの場合は Map とかにしてよしなにするだけですね.

class AppView extends StatelessWidget {
@override
Widget build(BuildContext context) {
var viewType = context.select<AppViewState, ViewType>(
(state) => state.viewType);
return WillPopScope(
onWillPop: () async {
if (viewType == ViewType.Main) {
Navigator.of(context).pop();
} else {
context
.read<AppViewStateNotifier>()
.setViewType(ViewType.Main);
}
return false;
},
child: Scaffold(
appBar: AppBar(
title: const Text('App'),
leading: viewType == ViewType.Main
? null
: IconButton(
icon: Icon(
Icons.arrow_back,
color: Colors.white,
),
onPressed: () {
context
.read<AppViewStateNotifier>()
.setViewType(ViewType.Main);
}),
),
body: Container(
child: viewType == ViewType.Main
? _MainView()
: _SubView(),
),
),
);
}
}

View を Sub に切り替えるときは ViewType.Sub を渡すだけです.

class _MainView extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
child: ListView(
children: <Widget>[
ListTile(
title: Text('Sub'),
onTap: () {
context
.read<AppViewStateNotifier>()
.setViewType(ViewType.Sub);
},
),
],
),
);
}
}

もっといいやりかたありそう.