Open18
Flutter関係のメモ書き
別Formの値をバリデーションで取得するサンプル
import 'package:flutter/material.dart';
class OriginalForm extends StatefulWidget {
const OriginalForm({Key? key}) : super(key: key);
_OriginalFormState createState() => _OriginalFormState();
}
class _OriginalFormState extends State<OriginalForm> {
final _formKey = GlobalKey<FormState>();
final _emailFormKey = GlobalKey<FormFieldState>();
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('test'),
),
body: Form(
key: _formKey,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
PartialForm(
formKey: _emailFormKey,
),
PartialConfirmationForm(
emailFormKey: _emailFormKey,
),
Padding(
padding: const EdgeInsets.symmetric(vertical: 16.0),
child: ElevatedButton(
onPressed: () {
if (_formKey.currentState != null &&
_formKey.currentState!.validate()) {
_formKey.currentState!.save();
}
},
child: const Text('Submit'),
),
),
],
),
),
);
}
}
class PartialForm extends StatelessWidget {
const PartialForm({
Key? key,
required this.formKey,
}) : super(key: key);
final Key formKey;
Widget build(BuildContext context) {
return TextFormField(
key: formKey,
decoration: const InputDecoration(
labelText: "Email address form",
hintText: 'Enter your email',
),
autovalidateMode: AutovalidateMode.disabled,
validator: (value) {
if (value == null || value.isEmpty) {
return 'Please enter some text';
}
return null;
},
onSaved: (value) => () {
print('$value');
},
);
}
}
class PartialConfirmationForm extends StatelessWidget {
const PartialConfirmationForm({
Key? key,
required this.emailFormKey,
}) : super(key: key);
final GlobalKey<FormFieldState> emailFormKey;
Widget build(BuildContext context) {
return TextFormField(
decoration: const InputDecoration(
labelText: "Confirmation",
hintText: 'Enter your email',
),
autovalidateMode: AutovalidateMode.disabled,
validator: (value) {
return TestValidator().isValid(value, emailFormKey);
},
);
}
}
class TestValidator {
String? isValid(String? value, GlobalKey<FormFieldState> emailFormKey) {
if (value == null || value.isEmpty) {
return 'Please enter some text';
}
final String email = emailFormKey.currentState!.value ?? '';
if (value != email) {
return 'not equall';
}
return null;
}
}
FormFieldを使って独自なフォームを作成したときにバリデーション文を出す方法
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
class SampleForm extends StatelessWidget {
const SampleForm({Key? key}) : super(key: key);
Widget build(BuildContext context) {
return Form(
child: OriginalForm(),
);
}
}
class OriginalForm extends FormField<int> {
OriginalForm({Key? key})
: super(
validator: (value) {
if (value! < 0) return 'Negative values not supported';
return null;
},
initialValue: 0,
autovalidateMode: AutovalidateMode.always,
builder: (FormFieldState<int> state) {
return Column(
children: <Widget>[
Row(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
IconButton(
icon: Icon(Icons.remove),
onPressed: () {
// Fieldの値を変化させる
state.didChange(state.value! - 1);
},
),
Text(state.value.toString()),
IconButton(
icon: Icon(Icons.add),
onPressed: () {
state.didChange(state.value! + 1);
},
),
],
),
state.hasError // Validatorの結果がエラー時の表示をここで対応
? Text(
state.errorText!,
style: const TextStyle(color: Colors.red),
)
: Container(),
],
);
},
);
}
- 引用元
state.hasErrorでバリデーションエラーが発生したときだけエラーを出すようにする
state.hasError // Validatorの結果がエラー時の表示をここで対応
? Text(
state.errorText!,
style: const TextStyle(color: Colors.red),
)
: Container(),
テキストを中央揃えする
index相互変換
var lists = [
{
'cd': '01',
'name': 'sample1',
},
{
'cd': '02',
'name': 'sample2',
},
{
'cd': '03',
'name': 'sample3',
},
];
// cdをnameに変換する
String toName(String cd) {
int? index = toIndex(cd);
if (index == null) {
return '';
}
return lists[index]['name'] ?? '';
}
// cdをindexに変換する
int? toIndex(String cd) {
int index = lists.indexWhere((list) => list['cd'] == cd);
return index != -1 ? index : null;
}
// indexをcdに変換する
String? toCd(int index) {
return lists.asMap().containsKey(index) ? lists[index]['cd'] : null;
}
void main() {
var cd = '03';
print(toName(cd));
print(toIndex(cd));
var key = 2;
print(toCd(key));
}
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
class TestPage extends HookWidget {
const TestPage({required this.testKey}) : super(key: testKey);
final Key testKey;
Widget build(
BuildContext context,
) {
final _height = MediaQuery.of(context).viewInsets.bottom;
final _scrollController = ScrollController();
useEffect(() {
WidgetsBinding.instance?.addPostFrameCallback((_) {
_scrollController.jumpTo(_scrollController.position.maxScrollExtent);
});
return () {
_scrollController.dispose();
};
}, []);
return Scaffold(
resizeToAvoidBottomInset: false,
appBar: AppBar(
title: const Text('遷移先の画面'),
),
body: SingleChildScrollView(
controller: _scrollController,
reverse: true,
child: Padding(
padding: EdgeInsets.only(
bottom: _height,
),
child: Column(
children: const [
OriginalForm(num: 1),
OriginalForm(num: 2),
OriginalForm(num: 3),
OriginalForm(num: 4),
OriginalForm(num: 5),
OriginalForm(num: 6),
OriginalForm(num: 7),
OriginalForm(num: 8),
OriginalForm(num: 9),
OriginalForm(num: 10),
OriginalForm(num: 11),
OriginalForm(num: 12),
],
),
),
),
);
}
}
class OriginalForm extends StatelessWidget {
const OriginalForm({Key? key, required this.num}) : super(key: key);
final int num;
Widget build(BuildContext context) {
return Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('form$num'),
TextFormField(),
],
);
}
}
import 'package:flutter/material.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
Widget build(BuildContext context) {
return const MaterialApp(
home: Dummy(),
);
}
}
class Dummy extends StatelessWidget {
const Dummy({
Key? key,
}) : super(key: key);
Widget build(BuildContext context) {
final List<GlobalKey<FormFieldState>> keys = List.generate(
30,
(index) => GlobalKey<FormFieldState>(),
);
final items = List.generate(
30,
(int index) => TextFormField(
key: keys[index],
initialValue: index.toString(),
),
);
return Scaffold(
appBar: AppBar(
title: const Text('dummyApp'),
),
// ListViewをSingleChildScrollViewでラップしている理由
// 特定のGlobalKeyを持つWidgetが表示されるようにスクロールする処理は、そのWidgetのcontextが必要
// contextはGlobakKey.currentContextで取得できる
// buildされないとWidgetツリーに登録されない関係でListView単体で使うと画面外のWidgetがbuildされないためのWidgetツリーに登録されない
// Widgetツリーに登録されないということはcontextを取得できない
// したがって、ListViewのchildren内の要素を一度すべてbuildさせるためにSingleChildScrollViewでラップしている
// https://github.com/flutter/flutter/issues/69190#issuecomment-718710989
// 追記: そもそもListView使わずにSingleScrollViewだけ使えばいい希ガス
body: SingleChildScrollView(
child: ListView(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
reverse: true,
children: items,
),
),
floatingActionButton: IconButton(
onPressed: () {
WidgetsBinding.instance?.addPostFrameCallback((_) {
final BuildContext? contx = keys.last.currentContext;
Scrollable.ensureVisible(
contx!,
curve: Curves.easeInOut,
alignment: 0.5,
alignmentPolicy: ScrollPositionAlignmentPolicy.explicit,
);
});
},
icon: const Icon(
Icons.arrow_circle_up_sharp,
size: 48,
color: Colors.blue,
),
),
);
}
}
Camera
class _Camera extends HookWidget {
const _Camera({
Key? key,
}) : super(key: key);
static show({
required BuildContext context,
}) {
Navigator.push(
context,
MaterialPageRoute(
fullscreenDialog: true,
builder: (_) => _Camera(),
),
);
}
Future<CameraController?> init() async {
final List<CameraDescription> _cameras = await availableCameras();
if (_cameras.isEmpty) {
return null;
}
final CameraController _controller = CameraController(
_cameras.first,
ResolutionPreset.high,
);
await _controller.initialize();
return _controller;
}
Widget build(BuildContext context) {
final snapshot = useFuture(useMemoized(() => init(), const []));
if (!snapshot.hasData) {
return const Center(
child: CircularProgressIndicator.adaptive(),
);
}
final CameraController cameraController = snapshot.data!;
return Scaffold(
appBar: AppBar(),
body: SizedBox(
width: double.maxFinite,
height: double.maxFinite,
child: CameraPreview(cameraController),
),
floatingActionButton: IconButton(
onPressed: () async {
final XFile pict = await cameraController.takePicture();
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(
pict.path,
),
),
);
Navigator.pop(context);
},
icon: const Icon(
Icons.camera_alt_rounded,
),
),
);
}
}
flutter cookie 管理
-
webview_cookie_manager を利用する場合
https://qiita.com/you0518/items/1c78e6022235066e54b6 -
dio_cookie_manager を利用する場合
https://qiita.com/koseidaiki/items/9a68b1406ee4b06b2c67
FCMを利用したプッシュ通知実装のサンプルコード