Related
I just started learning flutter I needed to pass data between pages so I found it easy to do with static variables but now I'm trying to make a setting page. I made a class named Settings like this :
class Settings {
static bool darkMode = false;
static bool addTable = true;
static saveSetting() {
GetStorage().write("darkMode", Settings.darkMode);
GetStorage().write("addTable", Settings.addTable);
}
static setSetting() {
GetStorage.init();
Settings.darkMode = (GetStorage().read("darkMode") ?? false);
Settings.addTable = (GetStorage().read("addTable") ?? true);
}
}
And a Switch in that page like this :
Switch(
value: Settings.addTable,
onChanged: (_) {
setState(() {
Settings.addTable = !Settings.addTable;
Settings.saveSetting();
});
}),
but after reloading the app the values are not saved in GetStorage, strings are saved perfectly except this one.
And the whole code is here :
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:get_storage/get_storage.dart';
import 'widgets/menu_button.dart';
void main() async {
await GetStorage.init();
DataManagament dataManagament = DataManagament();
dataManagament.startingApp();
runApp(const MyApp());
}
class DataManagament {
static String reserveString = "";
static List<String> reserveList = [];
static String tableString = "";
static List<String> tableList = [];
void saveReserveList() {
GetStorage().write("reserveList", reserveList.toString());
}
void saveTableList() {
GetStorage().write("tableList", tableList.toString());
}
void startingApp() {
Settings.setSetting;
//reserve
reserveString = (GetStorage().read("reserveList") ?? "");
reserveString == ""
? reserveList = []
: reserveList =
reserveString.substring(1, reserveString.length - 1).split(",");
//table
tableString = (GetStorage().read("tableList") ?? "");
tableString == ""
? tableList = []
: tableList =
tableString.substring(1, tableString.length - 1).split(",");
}
}
class Settings {
static bool darkMode = false;
static bool addTable = true;
static saveSetting() {
GetStorage().write("darkMode", Settings.darkMode);
GetStorage().write("addTable", Settings.addTable);
}
static setSetting() {
Settings.darkMode = (GetStorage().read("darkMode") ?? false);
Settings.addTable = (GetStorage().read("addTable") ?? true);
}
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return const MaterialApp(
home: IntroPage(),
debugShowCheckedModeBanner: false,
);
}
}
//
//
//************************************************************
//
//
//
//
//************************************************************
// Reserve Page
//
class ReservePage extends StatefulWidget {
const ReservePage({Key? key}) : super(key: key);
#override
State<ReservePage> createState() => _ReservePageState();
}
class _ReservePageState extends State<ReservePage> {
final _nameController = TextEditingController();
final _minuteController = TextEditingController();
final _hourController = TextEditingController();
#override
Widget build(BuildContext context) {
Size size = MediaQuery.of(context).size;
return Scaffold(
floatingActionButton: FloatingActionButton(
onPressed: () {
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
content: SingleChildScrollView(
child: Column(
children: [
TextField(
controller: _nameController,
textDirection: TextDirection.rtl,
decoration: const InputDecoration(
hintTextDirection: TextDirection.rtl,
border: OutlineInputBorder(),
labelText: 'نام',
),
),
Row(
children: [
Expanded(
flex: 5,
child: TextField(
controller: _hourController,
maxLength: 2,
keyboardType: TextInputType.number,
decoration: const InputDecoration(
labelText: 'hour',
),
),
),
const Expanded(
flex: 3,
child: Center(
child: Text(
":",
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
),
),
),
),
Expanded(
flex: 5,
child: TextField(
controller: _minuteController,
maxLength: 2,
keyboardType: TextInputType.number,
decoration: const InputDecoration(
labelText: 'minute',
),
),
),
],
),
const SizedBox(height: 20),
Center(
child: OutlinedButton(
onPressed: () {
if (_hourController.text != "" &&
_nameController.text != "") {
setState(() {
DataManagament.reserveList
.add(_nameController.text);
DataManagament.reserveList.add(
"${_hourController.text}:${_minuteController.text}");
_hourController.clear();
_nameController.clear();
_minuteController.clear();
DataManagament().saveReserveList();
});
}
},
child: const Text(
"save",
style: TextStyle(
color: Colors.black,
),
),
style: OutlinedButton.styleFrom(
minimumSize: Size(
size.width / 3,
(size.width / 3) * 0.4,
),
backgroundColor: Colors.green,
),
),
)
],
),
),
title: const Center(child: Text("")),
);
});
},
child: const FittedBox(
child: Icon(
Icons.add,
),
),
),
appBar: AppBar(
backgroundColor: Colors.white,
foregroundColor: Colors.black,
title: const Text(
"",
),
centerTitle: true,
),
body: DataManagament.reserveList.isNotEmpty
? SingleChildScrollView(
child: Column(
children: [
ListView.builder(
shrinkWrap: true,
itemCount: DataManagament.reserveList.length ~/ 2,
itemBuilder: (BuildContext context, int index) {
return Card(
// name 0 , time ,1
child: ListTile(
//name
trailing: Text(
DataManagament.reserveList[index * 2],
),
//time
title: Text(
DataManagament.reserveList[(index * 2) + 1],
),
leading: TextButton(
onPressed: () {
setState(() {
DataManagament.reserveList.removeAt(index * 2);
DataManagament.reserveList.removeAt(index * 2);
DataManagament().saveReserveList();
});
},
child: const Text("delete"),
),
));
},
),
],
),
)
: const Center(
child: Text("..."),
),
);
}
}
//
//
//************************************************************
// Menu Page
//
class MenuPage extends StatelessWidget {
const MenuPage({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
actions: [
IconButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const SettingsPage(),
));
},
icon: const Icon(Icons.settings),
),
],
backgroundColor: Colors.white,
foregroundColor: Colors.black,
centerTitle: true,
title: const Text(
"",
),
),
body: Column(
children: [
const Spacer(flex: 1),
Expanded(
child: Center(
child: MenuButton(
func: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const TablePage(),
));
},
text: "tables"),
),
flex: 2),
Expanded(
child: Center(
child: MenuButton(
func: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const ReservePage(),
));
},
text: "رزروها"),
),
flex: 2),
Expanded(
child: Center(
child: MenuButton(func: () {}, text: "test"),
),
flex: 2),
const Spacer(
flex: 4,
)
],
),
);
}
}
//
//
//************************************************************
// Tables Page
//
class TablePage extends StatefulWidget {
const TablePage({Key? key}) : super(key: key);
#override
State<TablePage> createState() => _TablePageState();
}
class _TablePageState extends State<TablePage> {
final _nameController = TextEditingController(); // ignore: unused_field
final _minuteController = TextEditingController(); // ignore: unused_field
final _hourController = TextEditingController(); // ignore: unused_field
#override
Widget build(BuildContext context) {
return Scaffold(
floatingActionButton: Settings.addTable
? GestureDetector(
onLongPress: () {
setState(() {
Settings.addTable = false;
});
},
child: FloatingActionButton(
onPressed: () {
setState(() {
});
},
child: const Icon(
Icons.add,
),
),
)
: null,
appBar: AppBar(
backgroundColor: Colors.white,
foregroundColor: Colors.black,
title: const Text(
"tables",
),
centerTitle: true,
),
body: DataManagament.tableList.isNotEmpty
? SingleChildScrollView(
child: Column(
children: [
ListView.builder(
shrinkWrap: true,
itemCount: DataManagament.tableList.length,
itemBuilder: (BuildContext context, int index) {
return Card(
// name 0 , time ,1
child: ListTile(
//name
trailing: Row(
children: [
Text(index.toString()),
TextButton(
onPressed: () {
setState(() {
DataManagament.tableList[index] =
_nameController.text;
});
},
child: const Text(""),
),
],
),
//time
title: TextButton(
onPressed: () {},
child: const Text(""),
),
leading: TextButton(
onPressed: () {},
child: Text(
DataManagament.tableList[index].toString()),
),
),
);
},
),
],
),
)
: const Center(
child: Text("..."),
),
);
}
}
//
//
//************************************************************
// Intro Page
//
class IntroPage extends StatefulWidget {
const IntroPage({Key? key}) : super(key: key);
#override
State<IntroPage> createState() => _IntroPageState();
}
class _IntroPageState extends State<IntroPage> {
Timer? _timer;
void startTimer() {
_timer = Timer(const Duration(seconds: 3), () {
Navigator.pushReplacement(
context,
MaterialPageRoute(
builder: (context) => const MenuPage(),
));
});
}
#override
void initState() {
super.initState();
startTimer();
}
#override
void dispose() {
_timer!.cancel();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: [
Expanded(
child: Center(
child: Image.asset("images/eightball.png"),
),
flex: 4),
const Expanded(
child: Center(
child: CircularProgressIndicator(),
),
),
],
),
);
}
}
//
//
//************************************************************
// Settings Page
//
class SettingsPage extends StatefulWidget {
const SettingsPage({Key? key}) : super(key: key);
#override
State<SettingsPage> createState() => _SettingsPageState();
}
class _SettingsPageState extends State<SettingsPage> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.white,
foregroundColor: Colors.black,
title: const Text("settings"),
),
body: ListView(children: [
Card(
child: ListTile(
trailing: Row(
mainAxisSize: MainAxisSize.min,
children: const [
Text(
"add table",
style: TextStyle(
fontWeight: FontWeight.bold,
),
),
SizedBox(width: 10),
Icon(Icons.add, size: 30),
],
),
leading: Switch(
value: Settings.addTable,
onChanged: (_) {
setState(() {
Settings.addTable = !Settings.addTable;
Settings.saveSetting();
});
}),
),
),
Card(
child: ListTile(
trailing: Row(
mainAxisSize: MainAxisSize.min,
children: const [
Text(
"night mode",
style: TextStyle(
fontWeight: FontWeight.bold,
),
),
SizedBox(width: 20),
Icon(Icons.dark_mode),
],
),
leading: Switch(
value: Settings.darkMode,
onChanged: (_) {
setState(() {
Settings.darkMode = !Settings.darkMode;
Settings.saveSetting();
});
}),
),
),
]),
);
}
}
You need to call
final settings = new Settings();
settings.setSettings();
in a page where you want to access settings
I think you are just reading data from GetStorage inside setSettings instead of writing to storage, so when the app releods or restarts data inside static variables will not be available. as:
static setSetting() {
GetStorage.init();
Settings.darkMode = (GetStorage().read("darkMode") ?? false);
Settings.addTable = (GetStorage().read("addTable") ?? true);
//Here you are just updating your settings variable.
//Update you storage also to keep the selection in storage
}
Hope it works.
I'm developing an application with flutter. But I cannot fix the buttons in the project. On my chat page, the button goes up. I'm new to the Flutter language, can you help me?
Hello, I'm developing an application with flutter. But I cannot fix the buttons in the project. On my chat page, the button goes up. I'm new to the Flutter language, can you help me?
Screenshot:
My Button Code :
class HomePage extends StatefulWidget {
#override
_HomeState createState() => _HomeState();
}
class _HomeState extends State<HomePage> {
final _scrollController = ScrollController();
List<TabItem> tabItems = List.of([
new TabItem(Icons.home, "Anasayfa", Colors.blue),
new TabItem(Icons.message, "Sohbet Odası", Colors.orange),
new TabItem(Icons.person, "Profil", Colors.red),
]);
int seciliPozisyon = 0;
CircularBottomNavigationController _navigationController;
#override
void initState() {
super.initState();
_navigationController =
new CircularBottomNavigationController(seciliPozisyon);
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.black,
centerTitle: true,
title: Text("Crypto App"),
),
body: Stack(
children: <Widget>[
Padding(
child: bodyContainer(),
padding: EdgeInsets.only(bottom: 60),
),
Align(alignment: Alignment.bottomCenter, child: bottomNav())
],
),
);
}
Widget bodyContainer() {
String activeUserId =
Provider.of<AuthorizationService>(context, listen: false).activeUserId;
Color selectedColor = tabItems[seciliPozisyon].color;
switch (seciliPozisyon) {
case 0:
return HomeScreen();
break;
case 1:
return FriendlyChatApp();
break;
case 2:
return Profile(
profileId: activeUserId,
);
break;
}
}
Widget bottomNav() {
return CircularBottomNavigation(
tabItems,
controller: _navigationController,
barHeight: 60,
barBackgroundColor: Colors.white,
animationDuration: Duration(milliseconds: 300),
selectedCallback: (int selectedPos) {
setState(() {
seciliPozisyon = selectedPos;
});
},
);
}
}
ChatApp Code :
void main() {
runApp(
FriendlyChatApp(),
);
}
final ThemeData kIOSTheme = ThemeData(
primarySwatch: Colors.blue,
primaryColor: Colors.grey[100],
primaryColorBrightness: Brightness.light,
);
final ThemeData kDefaultTheme = ThemeData(
primarySwatch: Colors.orange,
accentColor: Colors.orangeAccent,
);
String _name = '';
class FriendlyChatApp extends StatelessWidget {
const FriendlyChatApp({
Key key,
}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: ChatScreen(),
);
}
}
class ChatMessage extends StatelessWidget {
ChatMessage({this.text, this.animationController});
final String text;
final AnimationController animationController;
#override
Widget build(BuildContext context) {
return SizeTransition(
sizeFactor:
CurvedAnimation(parent: animationController, curve: Curves.easeOut),
axisAlignment: 0.0,
child: Container(
margin: EdgeInsets.symmetric(vertical: 10.0),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
margin: const EdgeInsets.only(right: 16.0),
child: CircleAvatar(child: Text(_name[0])),
),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(_name, style: Theme.of(context).textTheme.headline4),
Container(
margin: EdgeInsets.only(top: 5.0),
child: Text(text),
),
],
),
),
],
),
),
);
}
}
class ChatScreen extends StatefulWidget {
#override
_ChatScreenState createState() => _ChatScreenState();
}
class _ChatScreenState extends State<ChatScreen> with TickerProviderStateMixin {
final List<ChatMessage> _messages = [];
final _textController = TextEditingController();
final FocusNode _focusNode = FocusNode();
bool _isComposing = false;
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
decoration: Theme.of(context).platform == TargetPlatform.iOS //new
? BoxDecoration(
border: Border(
top: BorderSide(color: Colors.grey[200]),
),
)
: null,
child: Column(
children: [
Flexible(
child: ListView.builder(
padding: EdgeInsets.all(8.0),
reverse: true,
itemBuilder: (_, int index) => _messages[index],
itemCount: _messages.length,
),
),
Divider(height: 1.0),
Container(
decoration: BoxDecoration(color: Theme.of(context).cardColor),
child: _buildTextComposer(),
),
],
),
),
);
}
Widget _buildTextComposer() {
return IconTheme(
data: IconThemeData(color: Theme.of(context).accentColor),
child: Container(
margin: EdgeInsets.symmetric(horizontal: 8.0),
child: Row(
children: [
Flexible(
child: TextField(
controller: _textController,
onChanged: (String text) {
setState(() {
_isComposing = text.isNotEmpty;
});
},
onSubmitted: _isComposing ? _handleSubmitted : null,
decoration: InputDecoration.collapsed(
hintText: 'Mesajınızı Buraya Yazınız:'),
focusNode: _focusNode,
),
),
Container(
margin: EdgeInsets.symmetric(horizontal: 4.0),
child: Theme.of(context).platform == TargetPlatform.iOS
? CupertinoButton(
onPressed: _isComposing
? () => _handleSubmitted(_textController.text)
: null,
child: Text('Gönder'),
)
: IconButton(
icon: const Icon(Icons.send),
onPressed: _isComposing
? () => _handleSubmitted(_textController.text)
: null,
))
],
),
),
);
}
void _handleSubmitted(String text) {
_textController.clear();
setState(() {
_isComposing = false;
});
var message = ChatMessage(
text: text,
animationController: AnimationController(
duration: const Duration(milliseconds: 700),
vsync: this,
),
);
setState(() {
_messages.insert(0, message);
});
_focusNode.requestFocus();
message.animationController.forward();
}
#override
void dispose() {
for (var message in _messages) {
message.animationController.dispose();
}
super.dispose();
}
}
You need to add :
resizeToAvoidBottomInset: false,
Here as shown:
Scaffold(
//here
resizeToAvoidBottomInset: false,
appBar: AppBar(
backgroundColor: Colors.black,
centerTitle: true,
title: Text("Crypto App"),
),
body: Stack(
children: <Widget>[
Padding(
child: bodyContainer(),
padding: EdgeInsets.only(bottom: 60),
),
Align(alignment: Alignment.bottomCenter, child: bottomNav())
],
),
);
}
Here using this whenever user will type something setting value to false will make keyboard overlap the bottom navigation bar.
Hope this is what you wanted to achieve.
I'm trying to check if the keyboard is visible after tapping on the TextFormField by calling:
if (MediaQuery.of(context).viewInsets.bottom != 0) {
...
}
but as soon as I have this MediaQuery call in my code, the Keyboard doesn't even open anymore after tapping on the TextFormField...
Edited:
This is what happens when tapping on the TextFormField:
I added the code of the page which causes this faulty behavior:
class LearnPage extends StatefulWidget {
final int topicId;
final String topicName;
LearnPage(this.topicId, this.topicName);
#override
_LearnPageState createState() => _LearnPageState();
}
class _LearnPageState extends State<LearnPage> {
final mainCaardIndex = ValueNotifier<int>(0);
PageController _mainCaardController;
PageController _inputCaardController;
List<CaardM> caards;
List<PageM> mainCaardList = [];
List<List<PageM>> inputCaardList = [];
List<List<TextEditingController>> textControllers = [];
Future<void> async_init() async {
List<CaardM> caardList =
await DatabaseProviderCaard.db.getCaards(widget.topicId);
caards = caardList;
setState(() {});
}
bool _keyboardIsVisible() {
return !(MediaQuery.of(context).viewInsets.bottom == 0.0);
}
#override
void initState() {
async_init();
_mainCaardController = PageController();
_inputCaardController = PageController();
super.initState();
}
#override
void dispose() {
_mainCaardController.dispose();
_inputCaardController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.lightBlue,
title: Center(
child: Text(
widget.topicName,
textAlign: TextAlign.center,
style: TextStyle(fontWeight: FontWeight.bold),
),
),
actions: [
!_keyboardIsVisible()
? IconButton(
icon: Icon(Icons.check_circle_outline),
tooltip: 'Validate',
onPressed: validate,
)
: IconButton(
icon: Icon(Icons.keyboard_hide),
onPressed: () {
FocusManager.instance.primaryFocus.unfocus();
},
),
],
),
body: Column(
children: [
Expanded(
flex: 3,
child: FutureBuilder(
future: getMainContent(),
builder: (context, AsyncSnapshot<int> snapshotMain) {
if (snapshotMain.connectionState == ConnectionState.done) {
return PageView.builder(
itemCount: snapshotMain.data,
controller: _mainCaardController,
onPageChanged: (position) {
mainCaardIndex.value = position;
mainCaardIndex.notifyListeners();
_inputCaardController.jumpToPage(0);
},
itemBuilder: (context, position) {
return LearnMainCaard(
mainCaardList[position].title,
mainCaardList[position].content,
);
},
);
} else {
return CircularProgressIndicator();
}
},
),
),
Expanded(
flex: 5,
child: FutureBuilder(
future: getInputContent(),
builder: (context, AsyncSnapshot<int> snapshotInput) {
if (snapshotInput.connectionState == ConnectionState.done) {
return ValueListenableBuilder(
valueListenable: mainCaardIndex,
builder: (context, value, _) {
return PageView.builder(
itemCount: snapshotInput.data,
controller: _inputCaardController,
itemBuilder: (context, position) {
return LearnInputCaard(
inputCaardList[mainCaardIndex.value][position].title,
textControllers[mainCaardIndex.value][position],
);
},
);
},
);
} else {
return CircularProgressIndicator();
}
},
),
),
],
),
);
}
Future<int> getMainContent() async {
List<PageM> caardPages;
mainCaardList.clear();
for (var i = 0; i < caards.length; i++) {
caardPages = await DatabaseProviderPage.db.getPages(caards[i].id);
if (caards[i].pageAmount > 1) {
mainCaardList.add(caardPages[0]);
}
}
return mainCaardList.length;
}
Future<int> getInputContent() async {
List<PageM> caardPages = [];
List<PageM> list = [];
inputCaardList.clear();
for (var i = 0; i < caards.length; i++) {
caardPages = await DatabaseProviderPage.db.getPages(caards[i].id);
if (caards[i].pageAmount > 1) {
addController(caards[i].pageAmount - 1);
list = [];
for (var i = 1; i < caardPages.length; i++) {
list.add(caardPages[i]);
}
inputCaardList.add(list);
}
}
return inputCaardList[mainCaardIndex.value].length;
}
void addController(int controllerAmount) {
List<TextEditingController> currentTextControllers = [];
print('addController called');
currentTextControllers.clear();
currentTextControllers = List.generate(
controllerAmount, (index) => TextEditingController()
);
textControllers.add(currentTextControllers);
}
And here the LearnInputCaard widget:
import 'package:flutter/material.dart';
class LearnInputCaard extends StatefulWidget {
final String title;
final TextEditingController textController;
LearnInputCaard(
this.title,
this.textController,
);
#override
_LearnInputCaardState createState() => _LearnInputCaardState();
}
class _LearnInputCaardState extends State<LearnInputCaard> {
#override
Widget build(BuildContext context) {
return Container(
child: Card(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20),
),
margin: EdgeInsets.all(20),
color: Colors.amberAccent.shade100,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
children: [
Expanded(
flex: 1,
child: Text(
widget.title,
style: TextStyle(fontSize: 20),
),
),
Divider(color: Colors.black38,),
Expanded(
flex: 10,
child: Container(
padding: EdgeInsets.all(10.0),
child: TextFormField(
controller: widget.textController,
maxLines: 30,
decoration: InputDecoration(
hintText: "Enter content",
border: InputBorder.none,
),
),
),
)
],
),
),
),
);
}
}
you need to check MediaQuery.of(context).viewInsets.bottom == 0.0
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Keyboard Visibility Demo'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Container(
width: double.infinity,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
_keyboardIsVisible()
? Text(
"Keyboard is visible",
style: Theme.of(context)
.textTheme
.display1
.copyWith(color: Colors.blue),
)
: RichText(
text: TextSpan(children: [
TextSpan(
text: "Keyboard is ",
style: Theme.of(context)
.textTheme
.display1
.copyWith(color: Colors.blue),
),
TextSpan(
text: "not ",
style: Theme.of(context)
.textTheme
.display1
.copyWith(color: Colors.red),
),
TextSpan(
text: "visible",
style: Theme.of(context)
.textTheme
.display1
.copyWith(color: Colors.blue),
)
]),
),
SizedBox(
height: 20,
),
Container(
width: 200.0,
child: TextField(
style: Theme.of(context).textTheme.display1,
decoration: InputDecoration(
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(
color: Colors.blue,
),
borderRadius: BorderRadius.circular(10.0),
),
),
),
)
],
),
));
}
bool _keyboardIsVisible() {
return !(MediaQuery.of(context).viewInsets.bottom == 0.0);
}
}
The problem is that you get the context from the parent widget.
If you call:
MediaQuery.of(context);
in the same widget where your forms are, you shouldn't get this behavior.
You need to define a GlobalKey<FormState> in your highest widget and pass this one down. Then it works. I defined it first in my SafeArea and therefore it failed and I had the same problem with the keyboard.
Here are some snippets of my code. I have a PageController and use two different forms on my two pages.
class OnboardingScaffold extends HookConsumerWidget {
OnboardingScaffold({Key? key}) : super(key: key);
// here you define your GlobalKeys
final _formKeyLogin = GlobalKey<FormState>();
final _formKeyApply = GlobalKey<FormState>();
#override
Widget build(BuildContext context, WidgetRef ref) {
final controller = usePageController();
bool isKeyboard = MediaQuery.of(context).viewInsets.bottom != 0;
return Scaffold(
body: Container(
padding: !isKeyboard
? const EdgeInsets.only(bottom: 80)
: const EdgeInsets.only(bottom: 0),
child: PageView(
controller: controller,
children: [
// here you pass these keys into your child Widget
LoginSafeArea(
formKey: _formKeyLogin,
),
ApplySafeArea(
formKey: _formKeyApply,
),
],
),
),
bottomSheet: !isKeyboard
? Container(height: 80)
: Container(height: 0),
);
}
}
The child Widget should contain a Form Widget:
class LoginSafeArea extends HookConsumerWidget {
const LoginSafeArea({Key? key, required this.formKey}) : super(key: key);
final GlobalKey<FormState> formKey;
#override
Widget build(BuildContext context, WidgetRef ref) {
return SafeArea(
child: Center(
child: Form(
key: formKey,
child: SingleChildScrollView(
physics: const BouncingScrollPhysics(),
padding: const EdgeInsets.only(left: 24.0, right: 24.0),
child: Column(
children: <Widget>[
const EmailFieldWidget(),
const SizedBox(height: 8.0),
const PasswordFieldWidget(),
const SizedBox(height: 16.0),
LoginButtonWidget(
formKey: formKey,
),
const SizedBox(height: 8.0),
],
),
),
),
),
);
}
}
I've been trying to design the layout of my ExpansionTile just like the design below but I couldn't figure out how to change the layout. any suggestion on how to change the border radius, change the background color and also make a gap between each other?.
I tried adding boxDecoration in each container but the style only apply to outside but not on each expansionTile.
import 'package:flutter/material.dart';
class MyReoderWidget extends StatelessWidget {
#override
Widget build(BuildContext context) {
return ReorderItems(topTen: ['j']);
}
}
class DataHolder {
List<String> parentKeys;
Map<String, List<String>> childMap;
DataHolder._privateConstructor();
static final DataHolder _dataHolder = DataHolder._privateConstructor();
static DataHolder get instance => _dataHolder;
factory DataHolder.initialize({#required parentKeys}) {
_dataHolder.parentKeys = parentKeys;
_dataHolder.childMap = {};
for (String key in parentKeys) {
_dataHolder.childMap.putIfAbsent(
}
return _dataHolder;
}
}
class ReorderItems extends StatefulWidget {
final List<String> topTen;
ReorderItems({this.topTen});
#override
_ReorderItemsState createState() => _ReorderItemsState();
}
class _ReorderItemsState extends State<ReorderItems> {
#override
void initState() {
super.initState();
// initialize the children for the Expansion tile
// This initialization can be replaced with any logic like network fetch or something else.
DataHolder.initialize(parentKeys: widget.topTen);
}
#override
Widget build(BuildContext context) {
return PrimaryScrollController(
key: ValueKey(widget.topTen.toString()),
controller: ScrollController(),
child: Container(
decoration: BoxDecoration(),
child: ReorderableListView(
onReorder: onReorder,
children: getListItem(),
),
),
);
}
List<ExpansionTile> getListItem() => DataHolder.instance.parentKeys
.asMap()
.map((index, item) => MapEntry(index, buildTenableListTile(item, index)))
.values
.toList();
ExpansionTile buildTenableListTile(String mapKey, int index) => ExpansionTile(
key: ValueKey(mapKey),
title: Text(mapKey),
leading: Icon(Icons.list),
children: [
Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(20))
),
key: ValueKey('$mapKey$index'),
height: 200,
child: Container(
padding: EdgeInsets.only(left: 30.0),
child: ReorderList(
parentMapKey: mapKey,
),
),
),
],
);
void onReorder(int oldIndex, int newIndex) {
if (newIndex > oldIndex) {
newIndex -= 1;
}
setState(() {
String game = widget.topTen[oldIndex];
DataHolder.instance.parentKeys.removeAt(oldIndex);
DataHolder.instance.parentKeys.insert(newIndex, game);
});
}
}
class ReorderList extends StatefulWidget {
final String parentMapKey;
ReorderList({this.parentMapKey});
#override
_ReorderListState createState() => _ReorderListState();
}
class _ReorderListState extends State<ReorderList> {
#override
Widget build(BuildContext context) {
return PrimaryScrollController(
controller: ScrollController(),
child: ReorderableListView(
// scrollController: ScrollController(),
onReorder: onReorder,
children: DataHolder.instance.childMap[widget.parentMapKey]
.map(
(String child) => Container(
child: ListTile(
key: ValueKey(child),
leading: Icon(Icons.list),
title: Text(child),
),
),
)
.toList(),
),
);
}
void onReorder(int oldIndex, int newIndex) {
if (newIndex > oldIndex) {
newIndex -= 1;
}
List<String> children = DataHolder.instance.childMap[widget.parentMapKey];
String game = children[oldIndex];
children.removeAt(oldIndex);
children.insert(newIndex, game);
DataHolder.instance.childMap[widget.parentMapKey] = children;
// Need to set state to rebuild the children.
setState(() {});
}
}
You can do it using custom expandable container.
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Calendar',
theme: ThemeData(
primarySwatch: Colors.grey,
),
debugShowCheckedModeBanner: false,
home: Material(
child: MyReoderWidget(),
),
);
}
}
class CustomModel {
String title;
bool isExpanded;
List<String> subItems;
CustomModel({this.title, this.subItems, this.isExpanded = false});
}
class MyReoderWidget extends StatefulWidget {
#override
_MyReoderWidgetState createState() => _MyReoderWidgetState();
}
class _MyReoderWidgetState extends State<MyReoderWidget> {
List<CustomModel> listItems;
#override
void initState() {
super.initState();
listItems = List<CustomModel>();
listItems.add(CustomModel(
title: "App Name 1", subItems: ["Card Name 1", "Card Name 2"]));
listItems.add(CustomModel(
title: "App Name 2", subItems: ["Card Name 3", "Card Name 4"]));
}
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.black,
body: ListView(
children: listItems
.map((model) => new Padding(
padding: EdgeInsets.only(
bottom: 10,
),
child: ExpandableCardContainer(
isExpanded: model.isExpanded,
collapsedChild: createHeaderCard(model),
expandedChild: Column(
children: <Widget>[
Padding(
padding: EdgeInsets.only(
bottom: 10,
),
child: createHeaderCard(model),
)
]..addAll(model.subItems
.map((e) => createChildCard(e))
.toList()),
),
),
))
.toList()),
);
}
Widget createHeaderCard(CustomModel model) {
return Container(
child: Row(
children: <Widget>[
Icon(
Icons.more_vert,
color: Colors.white,
),
Expanded(
child: Text(
model.title,
style: TextStyle(color: Colors.white),
),
),
GestureDetector(
onTap: () {
setState(() {
model.isExpanded = !model.isExpanded;
});
},
child: Icon(
model.isExpanded
? Icons.keyboard_arrow_up
: Icons.keyboard_arrow_down,
color: Colors.white,
),
)
],
),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
color: Color(0xFF132435),
),
height: 50,
);
}
Widget createChildCard(String subItems) {
return Container(
margin: EdgeInsets.only(left: 30, bottom: 10),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Icon(
Icons.more_vert,
color: Colors.white,
),
Expanded(
child: Text(
subItems,
style: TextStyle(color: Colors.white),
),
),
],
),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
color: Color(0xFF132435),
),
height: 50,
);
}
}
class ExpandableCardContainer extends StatefulWidget {
final bool isExpanded;
final Widget collapsedChild;
final Widget expandedChild;
const ExpandableCardContainer(
{Key key, this.isExpanded, this.collapsedChild, this.expandedChild})
: super(key: key);
#override
_ExpandableCardContainerState createState() =>
_ExpandableCardContainerState();
}
class _ExpandableCardContainerState extends State<ExpandableCardContainer> {
#override
Widget build(BuildContext context) {
return new AnimatedContainer(
duration: new Duration(milliseconds: 200),
curve: Curves.easeInOut,
child: widget.isExpanded ? widget.expandedChild : widget.collapsedChild,
);
}
}
EDIT :
Here is my result now :
As you can see i have make a lot of work and now it is good Advanced. Now i have the 5 Numbers selected (5-34-37-42-49) in red just at top of the 2 green buttons. For the moment the function getWidget return the 5 Numbers in red using gridview again but not sure it is what i need to use. Can you help me for resolve the problem with the size of the 5 circles, i need it centered and not use scroll.
Here is my complete code Under :
import 'package:flutter/material.dart';
import 'dart:math';
import 'dart:convert';
import 'package:http/http.dart' as http;
import 'dart:async';
import 'package:flutter_app/menu_member.dart';
import 'package:flutter_app/globals.dart' as globals;
class Lotto extends StatefulWidget {
#override
_LottoState createState() => new _LottoState();
}
class _LottoState extends State<Lotto> {
#override
void initState() {
super.initState();
}
var i=1;
var nb_num=49;
var no_select=[];
var no_a_select=5;
List<Color> colorList = List<Color>.generate(49, (int index) => Colors.lightBlue);
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: new AppBar(
title: new Text('GRILLE DE LOTTO'),
),
body:
Center(
child: Column(
children: <Widget>[
Container(
width:400,
height:30,
margin: const EdgeInsets.only(top: 10.0),
child : new Text("Selectionnez 5 numéros",textAlign: TextAlign.center,style: TextStyle(fontSize: 30.0),),
),
Container(
width:400,
height:300,
child: new GridView.count(
crossAxisCount: 9,
padding: const EdgeInsets.all(30.0),
mainAxisSpacing: 4.0,
crossAxisSpacing: 4.0,
children: new List<Widget>.generate(49, (index) {
return new GestureDetector(
onTap: () {
setState(() {
if (colorList[index] == Colors.lightBlue) {
if (no_select.length<no_a_select) {
colorList[index] = Colors.redAccent;
no_select.add(index+1);
}
else {
showDialog(
context: context,
builder: (BuildContext context){
return AlertDialog(
title: Text("INFORMATION"),
content: Text("Vous ne pouvez pas sélectionner plus de 5 numéros !!!"),
);
}
);
}
print(no_select);
}
else {
colorList[index] = Colors.lightBlue;
no_select.remove(index+1);
print(no_select);
}
});
},
child: Container(
child: ClipOval(
child: Container(
color: colorList[index],
height: 20.0,
width: 20.0,
child: Center(
child: new Text((index+1).toString(),
style: TextStyle(color: Colors.white, fontSize: 24),
textAlign: TextAlign.center),
),
),
),
),
);
}
),
),
),
Container(
width:400,
height:30,
margin: const EdgeInsets.only(top: 10),
child : new Text("Vos Numéros",textAlign: TextAlign.center,style: TextStyle(fontSize: 30.0),),
),
Container(
width:400,
height:80,
margin: const EdgeInsets.only(top: 10.0),
decoration: BoxDecoration(
border: Border.all(
color: Colors.lightBlueAccent,
width: 2,
),
borderRadius: BorderRadius.circular(12),
),
child:
getWidget()
),
Container(
width:300,
height:45,
margin: const EdgeInsets.only(top: 10.0),
child:
RaisedButton(
color: Colors.green,
textColor: Colors.white,
padding: EdgeInsets.fromLTRB(9, 9, 9, 9),
child: Text('TIRAGE ALEATOIRE'),
onPressed: () {
Select_numbers();
},
),
),
Container(
width:300,
height:45,
margin: const EdgeInsets.only(top: 10.0),
child:
RaisedButton(
color: Colors.green,
textColor: Colors.white,
padding: EdgeInsets.fromLTRB(9, 9, 9, 9),
child: Text('VALIDER VOTRE GRILLE'),
onPressed: () {
Valide_grille();
},
),
),
]
)
),
),
);
}
getWidget() {
if (no_select.length==0) {
return Text("Pas de numéros");
}
else {
return GridView.count(
crossAxisCount: 5,
padding: const EdgeInsets.all(10.0),
mainAxisSpacing: 4.0,
crossAxisSpacing: 4.0,
children: new List<Widget>.generate(no_select.length, (index) {
return ClipOval(
child: Container(
color: Colors.red,
height: 20.0,
width: 20.0,
child: Center(
child: new Text((no_select[index].toString()),
style: TextStyle(color: Colors.white, fontSize: 24),
textAlign: TextAlign.center),
),
),
);
}
)
);
}
}
Select_numbers() {
setState(() {
var j = 1;
var num_sel;
var pos_sel;
no_select=[];
colorList=[];
colorList=List<Color>.generate(49, (int index) => Colors.lightBlue);
var rng = new Random();
List tab=[];
tab = List.generate(49, (int index) => index + 1);
print (tab);
while (j <= no_a_select) {
pos_sel = rng.nextInt(tab.length-1);
num_sel=tab[pos_sel];
no_select.add(num_sel);
colorList[num_sel-1] = Colors.redAccent;
tab.remove(num_sel);
print(tab);
j++;
}
print(no_select);
});
}
Future Valide_grille() async{
// For CircularProgressIndicator.
bool visible = false ;
// Showing CircularProgressIndicator.
setState(() {
visible = true ;
});
// SERVER LOGIN API URL
var url = 'https://www.easytrafic.fr/game_app/valide_lotto.php';
// Store all data with Param Name.
var data = {'id_membre':globals.id_membre, 'result':no_select};
print (data);
var grille_encode=jsonEncode(data);
print(grille_encode);
// Starting Web API Call.
var response = await http.post(url, body: grille_encode,headers: {'content-type': 'application/json','accept': 'application/json','authorization': globals.token});
print(response.body);
// Getting Server response into variable.
var message = json.decode(response.body);
// If the Response Message is Matched.
if(message == 'OK')
{
print('VALIDATION DE LA GRILLE OK');
// Hiding the CircularProgressIndicator.
setState(() {
visible = false;
});
}else{
// Hiding the CircularProgressIndicator.
setState(() {
visible = false;
});
// Showing Alert Dialog with Response JSON Message.
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: new Text(message),
actions: <Widget>[
FlatButton(
child: new Text("OK"),
onPressed: () {
Navigator.of(context).pop();
},
),
],
);
},
);
}
}
}
I think that you need a Flutter simple Alert Dialog instead of print command. So change your code :
print(
"Vous ne pouvez pas sélectionner plus de 5 numéros !!!");
to:
showDialog(
context: context,
builder: (BuildContext context){
return AlertDialog(
title: Text("Alert Dialog"),
content: Text("Vous ne pouvez pas sélectionner plus de 5 numéros !!!"),
);
}
);
Because print command sends its output to console.
I suggest you read this: https://dev.to/mightytechno/flutter-alert-dialog-to-custom-dialog-1ok4
Edit:
In order to have 49 circles between the buttons, you need move these lines of your code:
Expanded(
flex:2,
child:
RaisedButton(
color: Colors.green,
textColor: Colors.white,
padding: EdgeInsets.fromLTRB(9, 9, 9, 9),
child: Center(child: Text('TIRAGE ALEATOIRE')),
onPressed: () {
Select_numbers();
},
),
),
after these lines:
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: new AppBar(
title: new Text('GRILLE DE LOTTO'),
),
body:
Center(
child: Column(
children: <Widget>[
Also in order to make a RaisedButton's corners rounded, you can add this code:
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(40),),
after RaisedButton(
You can copy paste run full code below
You can use List to keep color of each number
code snippet
List<Color> colorList = List<Color>.generate(49, (int index) => Colors.lightBlue);
...
setState(() {
if (colorList[index] == Colors.lightBlue) {
if (no_select.length < no_a_select) {
colorList[index] = Colors.redAccent;
...
child: Container(
color: colorList[index],
working demo
full code
import 'package:flutter/material.dart';
class Lotto extends StatefulWidget {
#override
_LottoState createState() => new _LottoState();
}
class _LottoState extends State<Lotto> {
Color color;
void message() {
print('Clicked');
}
List<Color> colorList = List<Color>.generate(49, (int index) => Colors.lightBlue);
#override
void initState() {
super.initState();
color = Colors.lightBlue;
print(colorList[0].toString());
}
var i = 1;
var nb_num = 49;
var no_select = [];
var no_a_select = 5;
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: Center(
child: Column(
children: <Widget>[
Expanded(
flex: 3,
child: new GridView.count(
crossAxisCount: 7,
children: new List<Widget>.generate(49, (index) {
return new GestureDetector(
onTap: () {
setState(() {
if (colorList[index] == Colors.lightBlue) {
if (no_select.length < no_a_select) {
colorList[index] = Colors.redAccent;
no_select.add(index + 1);
} else {
print(
"Vous ne pouvez pas sélectionner plus de 5 numéros !!!");
}
print(no_select);
} else {
colorList[index] = Colors.lightBlue;
no_select.remove(index + 1);
print(no_select);
}
});
},
child: Container(
child: ClipOval(
child: Container(
color: colorList[index],
height: 20.0,
width: 20.0,
child: Center(
child: new Text((index + 1).toString(),
style: TextStyle(color: Colors.white, fontSize: 24),
textAlign: TextAlign.center),
),
),
),
),
);
}),
),
),
Expanded(flex: 1, child: Text("abc")),
],
),
),
),
);
}
}
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: Lotto(),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
void _incrementCounter() {
setState(() {
_counter++;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.headline4,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}