Related
I have a Bottom Navigation in parent widget, and a few textfields in child widget. When user clicks on the navigation tab and if one of the textfields is empty, it will set focus on the particular textfields.
I am using the constructor method learnt from one of the developer however I couldn't get it work. It seems like I didn't pass over the context properly. I am not sure.
Anyone able to spot my mistakes or advise other methods which can achieve the same result?
login.dart
class Login extends StatefulWidget{
#override
State<StatefulWidget> createState() {
return _LoginState();
}
}
class _LoginState extends State<Login> {
FocusNode focusNode;
Page1 focus;
#override
void initState() {
super.initState();
focusNode = new FocusNode();
focus = new Page1(focusNode: focusNode);
}
int currentBottomNavIndex = 0;
List<Widget> bottomNav = [
Page1(),
Page2(),
];
onTapped(int index) {
//if(textfield not empty) {
//setState(() {
//currentBottomNavIndex = index;
//});
//}else {
focus.setFocus(context);
//}
}
#override
Widget build(BuildContext context){
return new Scaffold(
appBar: new AppBar(
title: Text('Login Page'),
),
body: bottomNav[currentBottomNavIndex],
bottomNavigationBar: BottomNavigationBar(
onTap: onTapped,
//onTap: requestFocus(context),
currentIndex: currentBottomNavIndex,
type: BottomNavigationBarType.fixed,
items: [
BottomNavigationBarItem(
icon: Icon(Icons.home),
title: Text("Page1"),
),
BottomNavigationBarItem(
icon: Icon(Icons.mail),
title: Text('Page2'),
),
],
),
);
}
}
page1.dart
class Page1 extends StatefulWidget {
final FocusNode focusNode;
const Page1({Key key, this.focusNode}) : super(key: key);
void setFocus(BuildContext context) {
print("$focusNode requestFocus...");
FocusScope.of(context).requestFocus(focusNode);
}
#override
State<StatefulWidget> createState() {
return _Page1State();
}
}
class _Page1State extends State<Page1> {
TextEditingController name1 = TextEditingController();
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
return new Scaffold(
body: SingleChildScrollView(
child: Column(
children: <Widget>[
nameApp(),
],
)
)
);
}
Widget nameApp(){
return Container(
margin: EdgeInsets.all(50.0),
//width: 185,
child: Center(
child: Row(
children: [
Container(
child: Text("Name :", style: TextStyle(fontSize: 15), ),
),
Container(
child: Flexible(
child: TextField(
focusNode: widget.focusNode,
controller: name1,
onTap: (){
name1.clear();
},
onChanged: (String str){
},
textAlign: TextAlign.center,
decoration: InputDecoration(
contentPadding: EdgeInsets.symmetric(vertical: 5),
hintText: "Full Name",
hintStyle: TextStyle(fontSize: 14),
),
),
),
),
]
)
)
);
}
}
When user click on the bottom tab, I expect to see the textfield is in focus however nothing happen.
I noticed the method in child widget has been called:
flutter: FocusNode#419f4 requestFocus...
flutter: FocusNode#419f4(FOCUSED) requestFocus...
however the textfield is still not focus.
I've create a simple sample project for this and its works for me just fine.
Please check out my solution:
The HomePage:
import 'package:flutter/material.dart';
import 'package:focus_node/widgets/MyInputWidget.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 Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
FocusNode field1FocusNode = FocusNode(); //Create first FocusNode
FocusNode field2FocusNode = FocusNode(); //Create second FocusNode
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Padding(
padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 35),
child: MyInputWidget(
focusNode: field1FocusNode, //Provide the first FocusNode in the constructor
hint: "Email",
onEditCompleted: (){
FocusScope.of(context).requestFocus(field2FocusNode); //Request focus
},
),
),
Padding(
padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 35),
child: MyInputWidget(
focusNode: field2FocusNode, //Provide the second FocusNode
hint: "Password",
onEditCompleted: (){
FocusScope.of(context).requestFocus(field1FocusNode); //Request focus
},
),
)
],
),
),
);
}
}
The Custom Widget required focus:
class MyInputWidget extends StatefulWidget {
final FocusNode focusNode;
final String hint;
final VoidCallback onEditCompleted;
MyInputWidget({this.focusNode, this.hint, this.onEditCompleted});
#override
_MyInputWidgetState createState() => _MyInputWidgetState();
}
class _MyInputWidgetState extends State<MyInputWidget> {
#override
Widget build(BuildContext context) {
return Container(
padding: EdgeInsets.all(8),
child: TextField(
focusNode: widget.focusNode, //The FocusNode provided by the parent widget
decoration: InputDecoration(
hintText: widget.hint
),
onEditingComplete: widget.onEditCompleted,
),
);
}
}
Hope this helps.
I am trying to display some widgets like a CheckBox or a Switch in an OverlayEntry. The overlay is built in a tap event.
The problem is that the bool _value is updated only the first time I tap the CheckBox inside the overlay, but that CheckBox doesn't update its State. The strange thing is that (only the first tap) the tap updates the CheckBox outside the overlay instead of the one inside it.
What I have missed here?
Below a full snippet to reproduce this.
Thanks for your time!
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key}) : super(key: key);
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
bool _value = true;
_buildOverlay() {
OverlayEntry _overlayEntry;
OverlayState _overlayState = Overlay.of(context);
_overlayEntry = OverlayEntry(
builder: (BuildContext context) {
return Stack(
children: <Widget>[
Material(
child: Container(
padding: EdgeInsets.all(100),
color: Colors.lightBlue,
child: Checkbox(
value: _value,
onChanged: (bool value) { print("$value $_value"); setState(() => _value = value); },
),
),
),
],
);
},
);
_overlayState.insert(_overlayEntry);
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(""),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Row(
children: <Widget>[
FloatingActionButton(
onPressed: () {_buildOverlay();},
child: Icon(Icons.add),
),
Checkbox(
value: _value,
onChanged: (bool value) { print("$value $_value"); setState(() => _value = value); },
),
],
),
],
),
),
);
}
}
UPDATE:
In addition to the anmol.majhail solution, using ValueListenableBuilder could be another solution without the need to do a StatefulWidget.
The declaration of _value becomes:
var _value = ValueNotifier<bool>(false);
and here the _overlayEntry in the _buildOverlay() function:
_overlayEntry = OverlayEntry(
builder: (BuildContext context) {
return Material(
child: Container(
padding: EdgeInsets.all(100),
color: Colors.lightBlue,
child: ValueListenableBuilder<bool>(
valueListenable: _value,
builder: (context, value, child) {
return Checkbox(
value: _value.value,
onChanged: (bool value) {
print("$value $_value");
setState(() => _value.value = value);
},
);
},
),
),
);
},
);
Issue here is Overlay entry have different context. In Order to make it work you need to separate the overlay entry in a separate stateful widget - through which you can manage the checkbox state.
working Code:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key}) : super(key: key);
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
bool _value = true;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(""),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Row(
children: <Widget>[
FloatingActionButton(
onPressed: () {
OverlayState _overlayState = Overlay.of(context);
_overlayState.insert(OverlayEntry(
builder: (BuildContext context) {
return OverlayClass(
val: _value,
);
},
));
// _buildOverlay(context);
},
child: Icon(Icons.add),
),
Checkbox(
value: _value,
onChanged: (bool value) {
print("$value $_value");
setState(() => _value = value);
},
),
],
),
],
),
),
);
}
}
class OverlayClass extends StatefulWidget {
final bool val;
OverlayClass({this.val});
#override
_OverlayClassState createState() => _OverlayClassState();
}
class _OverlayClassState extends State<OverlayClass> {
bool _value;
#override
void initState() {
super.initState();
_value = widget.val;
}
#override
Widget build(BuildContext context) {
return Stack(
children: <Widget>[
Material(
child: Container(
padding: EdgeInsets.all(100),
color: Colors.lightBlue,
child: Checkbox(
value: _value,
onChanged: (bool value) {
print("$value $_value");
setState(() => _value = value);
},
),
),
),
],
);
}
}
I'm making Notes app. I made cards with text and buttons dynamically (Create by clicking the button). But I have problem with Changing Text on CURRENT card. For example, I have 3 cards with own texts and buttons and I want to change text on 2nd card but text is changing on the 3rd card. How can I solve this problem?
3 cards with texts and buttons
Change Text Page
In the past, I've tried making list to collect texts, but i dont know how to identify current card.
full main.dart
import 'package:flutter/material.dart';
import './changeTextPage.dart';
int count = 0;
String titlecard = '';
String textcard = '';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Notes',
theme: ThemeData(
primarySwatch: Colors.deepPurple
),
home: HomePage(title: 'Notes',),
);
}
}
class HomePage extends StatefulWidget {
HomePage({Key key, this.title}) : super(key: key);
final title;
#override
HomePageState createState() => HomePageState();
}
class HomePageState extends State<HomePage> {
#override
Widget build(BuildContext context) {
List<Widget> cards = new List.generate(count, (int i) => new MyCard());
return Scaffold(
appBar: AppBar(
title: Text('Notes'),
),
body: LayoutBuilder(
builder: (context, constraint) {
return Column(
children: <Widget>[
Container(
height: 650.0,
child: new ListView(
children: cards,
scrollDirection: Axis.vertical,
),
),
],
);
}
),
floatingActionButton: FloatingActionButton(
child: Icon(Icons.add),
onPressed: () {
setState(() {
Navigator.push(context, MaterialPageRoute(
builder: (context) => changeText())
);
});
},
),
);
}
}
class MyCard extends StatefulWidget {
#override
myCard createState() => myCard();
}
class myCard extends State<MyCard> {
int myCount = count;
void click() {
setState(() {
Navigator.push(context, MaterialPageRoute(
builder: (context) => setNewText())
);
});
}
#override
Widget build(BuildContext context) {
return Center(
child: Card(
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
ListTile(
leading: Icon(Icons.album),
title: Text(titlecard),
subtitle: Text(textcard),
),
ButtonTheme.bar( // make buttons use the appropriate styles for cards
child: ButtonBar(
children: <Widget>[
FlatButton(
child: const Text('Change Text'),
onPressed: click,
),
FlatButton(
child: const Text('LISTEN'),
onPressed: () { /* ... */ },
),
],
),
),
],
),
),
);
}
}
class setNewText extends StatefulWidget {
#override
SetNewText createState() => SetNewText();
}
class SetNewText extends State<setNewText> {
final titleController = TextEditingController();
final textController = TextEditingController();
final formkey = GlobalKey<FormState>();
void _submit() {
setState(() {
if (formkey.currentState.validate()) {
formkey.currentState.save();
Navigator.pop(context);
titlecard = titleController.text;
textcard = textController.text;
}
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Change Title'),
),
body: Column(
children: <Widget>[
Card(
child: Padding(
padding: EdgeInsets.all(2.0),
child: Form(
key: formkey,
child: Column(
children: <Widget>[
TextFormField(
controller: titleController,
decoration: InputDecoration(
labelText: 'Title'
),
validator: (value) => value.length < 1 ? 'Invalid Title' : null,
onSaved: (value) => value = titleController.text,
),
TextFormField(
controller: textController,
decoration: InputDecoration(
labelText: 'Text'
),
validator: (text) => text.length < 1 ? 'Invalid Text' : null,
onSaved: (text) => text = textController.text,
)
],
),
),
),
),
FlatButton(
textColor: Colors.deepPurple,
child: Text('SUBMIT'),
onPressed: _submit,
),
],
)
);
}
}
changeTextPage.dart
import 'package:flutter/material.dart';
import './main.dart';
class changeText extends StatefulWidget {
#override
ChangeText createState() => ChangeText();
}
class ChangeText extends State<changeText> {
myCard s = myCard();
final titleController = TextEditingController();
final textController = TextEditingController();
final formkey = GlobalKey<FormState>();
void _submit() {
setState(() {
if (formkey.currentState.validate()) {
formkey.currentState.save();
Navigator.pop(context);
count++;
titlecard = titleController.text;
textcard = textController.text;
}
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Change Title'),
),
body: Column(
children: <Widget>[
Card(
child: Padding(
padding: EdgeInsets.all(2.0),
child: Form(
key: formkey,
child: Column(
children: <Widget>[
TextFormField(
controller: titleController,
decoration: InputDecoration(
labelText: 'Title'
),
validator: (value) => value.length < 1 ? 'Invalid Title' : null,
onSaved: (value) => value = titleController.text,
),
TextFormField(
controller: textController,
decoration: InputDecoration(
labelText: 'Text'
),
validator: (text) => text.length < 1 ? 'Invalid Text' : null,
onSaved: (text) => text = textController.text,
)
],
),
),
),
),
FlatButton(
textColor: Colors.deepPurple,
child: Text('SUBMIT'),
onPressed: _submit,
),
],
)
);
}
}
Okay, so you happen to make some common mistakes, one of which is critical.
most importantly don't use global variables! As you do with count, titlecard and textcard.
there is a practice to name stateful widgets with PascalCase and corresponding states just like the widget but prefixed with an underscore (_) to make it private and suffixed by the State word.
The correct approach for this (or one of them) would be to have a widget that would be your screen with a form to edit stuff and it would pop some struct with user values on submit:
class ChangeTextScreen extends StatefulWidget {
_ChangeTextScreenState createState() => _ChangeTextScreenState();
}
class _ChangeTextScreenState extends State<ChangeTextScreen> {
void _submit() {
setState(() {
formkey.currentState.save();
Navigator.pop(ChangeTextResult(title: titleController.text, text: textController.text));
});
}
// Rest of your code...
}
class ChangeTextResult {
final String title;
final String text;
ChangeTextResult({#required this.title, #required this.text});
}
You should also have a place where you store your notes in some kind of a list. Your main screen looks like a good place for it. Once your app will be bigger, think about using scoped_model or Redux or something.
So let's add a Note class and a list with your notes to your main screen:
class Note {
String title;
String text;
Note(this.title, this.text);
}
class HomePageState extends State<HomePage> {
List<Note> _notes = [Note('Test', 'Some test note')];
#override
Widget build(BuildContext context) {
ListView cards = ListView.builder(
itemCount: _notes.length,
itemBuilder: (context, index) => MyCard(
title: _notes[index].title,
text: _notes[index].text,
onEdit: (title, text) => setState(() { // We'll get back to that later
_notes[index].title = title;
_notes[index].text = text;
})
));
// (...)
Your MyCard widget (try to use better names next time) should contain some kind of information about its content, one of the best approaches would be to pass this info to its constructor, just like that:
class MyCard extends StatefulWidget {
final String title;
final String text;
final Function onEdit;
MyCard({Key key, #required this.title, #required this.text, #required this.onEdit}) : super(key: key);
#override
_MyCardState createState() => _MyCardState();
}
Having this Key parameter is a good practice.
And use those parameters in your _MyCardState class (I renamed it from _myCard):
// (...)
children: <Widget>[
ListTile(
leading: Icon(Icons.album),
title: Text(widget.title),
subtitle: Text(widget.text),
),
// (...)
Returning to the moment where you open your ChangeTextScreen, you should assign the result of Navigation.push() to a variable. This is your result, you can deal with it (once we check it for null, the user could have returned from this screen and then the result would be null).
void click() {
setState(() {
final ChangeTextResult result = Navigator.push(context, MaterialPageRoute(
builder: (context) => ChangeTextScreen())
);
if (result != null) {
widget.onEdit(result.title, result.text);
}
});
}
Do you remember that onEdit parameter (I mentioned it in a comment in the code above)? We call that parameter here.
That's it I think. I could have mixed some concepts of your app, but I think you'll manage to get my point anyways.
I quite rewrote all of your code. I think it will be easier for you to start again from scratch and have those tips in mind. Also, try to Google some similar things (like a simple Todo application) or do Getting started from flutter.io with part two! That should give you a nice idea on how to resolve that common problem in Flutter.
And also, read about good practises in Flutter and Dart. Things like correctly formatting your code are really important.
BTW that's my longest answer on Stack Overflow so far. I hope you'll appreciate that.
I'm trying to create an application to display the results of a search. I have the following extremely simple StatefulWidget. It has a single state variable data, which is passed in the constructor.
class Entry extends StatefulWidget {
Entry({Key key, #required this.data}) : super(key: key);
final String data;
#override
_EntryState createState() => _EntryState(data);
}
class _EntryState extends State<Entry> {
_EntryState(this.data) : super();
final String data;
#override
Widget build(BuildContext context) {
return Text(data);
}
}
To generate datasets of random length (between 0-9) I use the following helper function, which basically keeps track of the DateTime and the string that is passed to it:
List<String> render(String searchTerm) {
final Random rng = new Random();
final String date = DateTime.now().toString();
return List<String>.generate(
rng.nextInt(10),
(int i) => '$searchTerm $i $date',
);
}
Lastly, my main widget is as such. It uses a state variable _items to keep track of a list of Entrys. One can do a new query through a search bar, which could return a dataset of random length. The list is then displayed through a ListView.
class MyWidget extends StatefulWidget {
MyWidget({Key key}) : super(key: key);
final String title = "Demo";
#override
_State createState() => _State();
}
class _State extends State<MyWidget> {
List<String> _items = [];
Future<Null> getData(String searchTerm) async {
final entriesToAdd = render(searchTerm);
setState(() {
_items = entriesToAdd;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: TextField(
onSubmitted: getData,
decoration:
InputDecoration(hintText: 'Search', icon: Icon(Icons.search)),
),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Expanded(
child: ListView.builder(
itemCount: _items.length,
itemBuilder: (context, index) {
return Container(
padding: EdgeInsets.all(8.0), child: Entry(data: _items[index]));
})),
],
),
));
}
}
So my issue is that I expect that when I do a search through the button (i.e. call getData), the list displayed will be updated with the results of the new query, completely overwriting the old ones. This is not what happens. The displayed list is the correct length, but the items are often a mix of various previous/current searches like the following screenshot, which shows a mix of 2 searches. However, when doing print statements in the itemBuilder function of the ListView, the _item variable seems to be updating correctly, and regardless there shouldn't really be a mix of 2 different search results. What's causing this behavior?
Here's the full code:
import 'dart:async';
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
void main() => runApp(App());
class App extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Demo',
theme: ThemeData(
primarySwatch: Colors.green,
),
initialRoute: '/',
routes: <String, WidgetBuilder>{
'/': (BuildContext context) => MyWidget(),
},
);
}
}
class MyWidget extends StatefulWidget {
MyWidget({Key key}) : super(key: key);
final String title = "Demo";
#override
_State createState() => _State();
}
class _State extends State<MyWidget> {
List<String> _items = [];
Future<Null> getData(String searchTerm) async {
final entriesToAdd = render(searchTerm);
setState(() {
_items = entriesToAdd;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: TextField(
onSubmitted: getData,
decoration:
InputDecoration(hintText: 'Search', icon: Icon(Icons.search)),
),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Expanded(
child: ListView.builder(
itemCount: _items.length,
itemBuilder: (context, index) {
return Container(
padding: EdgeInsets.all(8.0), child: Entry(data: _items[index]));
})),
],
),
));
}
}
class Entry extends StatefulWidget {
Entry({Key key, #required this.data}) : super(key: key);
final String data;
#override
_EntryState createState() => _EntryState(data);
}
class _EntryState extends State<Entry> {
_EntryState(this.data) : super();
final String data;
#override
Widget build(BuildContext context) {
return Text(data);
}
}
List<String> render(String searchTerm) {
final Random rng = new Random();
final String date = DateTime.now().toString();
return List<String>.generate(
rng.nextInt(10),
(int i) => '$searchTerm $i $date',
);
}
Don't cache Widgets! You should be building them fresh on each call to build. (If you are worried about this being inefficient, don't. This is how Flutter is designed and heavily optimized to support this.) Just keep the list of Strings and re-build the Texts each time.
Avoid classes with just static functions. These can be replaced with top-level functions.
Try this instead:
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
void main() => runApp(App());
class App extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Demo',
theme: ThemeData(
primarySwatch: Colors.green,
),
initialRoute: '/',
routes: <String, WidgetBuilder>{
'/': (BuildContext context) => const MyWidget(),
},
);
}
}
class MyWidget extends StatefulWidget {
const MyWidget({Key key}) : super(key: key);
#override
MyWidgetState createState() => MyWidgetState();
}
class MyWidgetState extends State<MyWidget> {
List<String> _items = <String>[];
void getData(String searchTerm) {
final List<String> newEntries = helper(searchTerm);
setState(() {
_items = newEntries;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: TextField(
onSubmitted: getData,
decoration: const InputDecoration(
hintText: 'Search',
icon: Icon(Icons.search),
),
),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Expanded(
child: ListView.builder(
itemCount: _items.length,
itemBuilder: (BuildContext context, int index) => Container(
padding: const EdgeInsets.all(8.0),
child: Text(_items[index]),
),
),
),
],
),
),
);
}
}
List<String> helper(String searchTerm) {
final Random rng = new Random();
final String date = DateTime.now().toString();
return List<String>.generate(
rng.nextInt(10),
(int i) => '$searchTerm $i $date',
);
}
Here's a version that illustrates using StatefulWidget:
class MyWidget extends StatefulWidget {
const MyWidget({Key key}) : super(key: key);
#override
MyWidgetState createState() => MyWidgetState();
}
class MyWidgetState extends State<MyWidget> {
List<String> _items = <String>[];
void getData(String searchTerm) {
final List<String> newEntries = helper(searchTerm);
setState(() {
_items = newEntries;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: TextField(
onSubmitted: getData,
decoration: const InputDecoration(
hintText: 'Search',
icon: Icon(Icons.search),
),
),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Expanded(
child: ListView.builder(
itemCount: _items.length,
itemBuilder: (BuildContext context, int index) => Container(
padding: const EdgeInsets.all(8.0),
child: Entry(data: _items[index]),
),
),
),
],
),
),
);
}
}
class Entry extends StatefulWidget {
const Entry({Key key, #required this.data}) : super(key: key);
final String data;
#override
_EntryState createState() => _EntryState();
}
class _EntryState extends State<Entry> {
bool bold = true;
#override
Widget build(BuildContext context) => GestureDetector(
onTap: () {
setState(() {
bold = !bold;
});
},
child: Text(
widget.data,
style: TextStyle(
fontWeight: bold ? FontWeight.bold : FontWeight.normal,
),
),
);
}
This is what I am trying to achieve.
I tried adding a Stack to my bottom navigation bar item, and using negative values in a Positioned widget, but this doesn't work as it gets cutoff at the top of the navigation bar.
Here is the code for my BottomNavigationBarItem. Right now I am using just a red dot to try and get it above the button.
new BottomNavigationBarItem(
icon: new Stack(
overflow: Overflow.visible,
children: <Widget>[
new Icon(Icons.home),
new Positioned(
top: -5.0,
right: 0.0,
child: new Icon(Icons.brightness_1, size: 8.0,
color: Colors.redAccent),
)
]
),
title: new Container(),
backgroundColor: Colors.white),
You can try this
import 'package:flutter/material.dart';
void main() => runApp(new MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'Flutter Demo',
theme: new ThemeData(
primarySwatch: Colors.blue,
canvasColor: Colors.blue
),
home: new MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => new _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
ValueNotifier<int> bottomNavNotifier = new ValueNotifier(0);
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text(widget.title),
),
bottomNavigationBar: new Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
new BottomNavHighlight(bottomNavNotifier),
new BottomNavigationBar(
type: BottomNavigationBarType.fixed,
fixedColor: Colors.white,
items: <BottomNavigationBarItem>[
new BottomNavigationBarItem(icon: new Icon(Icons.create), title: new Text("Create")),
new BottomNavigationBarItem(icon: new Icon(Icons.create), title: new Text("Create")),
new BottomNavigationBarItem(icon: new Icon(Icons.create), title: new Text("Create")),
new BottomNavigationBarItem(icon: new Icon(Icons.create), title: new Text("Create"))
],
onTap: (int index){
print(index);
bottomNavNotifier.value = index;
},
)
],
),
backgroundColor: Colors.white,// This trailing comma makes auto-formatting nicer for build methods.
);
}
}
class BottomNavHighlight extends StatefulWidget{
final ValueNotifier<int> activeIndex;
BottomNavHighlight(this.activeIndex);
#override
State createState() {
return new _BottomNavHighlightState();
}
}
class _BottomNavHighlightState extends State<BottomNavHighlight>{
#override
Widget build(BuildContext context) {
List<Widget> items = <Widget>[
new Expanded(child: new Container()),
new Expanded(child: new Container()),
new Expanded(child: new Container()),
];
items.insert(
widget.activeIndex.value,
new Expanded(child: new Container(child: new Icon(Icons.play_circle_outline, size: 40.0,))),);
return new Row(
children: items,
);
}
#override
void initState() {
super.initState();
widget.activeIndex.addListener((){
setState(() {
});
});
}
#override
void dispose() {
super.dispose();
widget.activeIndex.dispose();
}
}
import 'package:flutter/material.dart';
class Home extends StatefulWidget {
#override
_HomeState createState() => _HomeState();
}
class _HomeState extends State<Home> with SingleTickerProviderStateMixin {
TabController tabController;
int _index = 0;
List<String> tabString = [
'Discover',
'Geners',
'Artists'
];
String _title = 'Discover';
#override
void initState() {
super.initState();
tabController = TabController(
length: 3,
vsync: this
);
this._index = 0;
setState(() {
this._title = tabString.first;
});
}
#override
void dispose() {
tabController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(_title),
actions: <Widget>[
IconButton(
icon: Icon(Icons.search),
onPressed: (){},
)
],
),
body: new Container(
color: const Color(0xffEEEEEE),
child: TabBarView(
children: <Widget>[
Container(child:Text('1')),
Container(child:Text('2')),
Container(child:Text('3')),
],
controller: tabController,
),
),
bottomNavigationBar: BottomNavigationBar(
type: BottomNavigationBarType.fixed,
currentIndex: _index,
onTap: (int _index) {
setState(() {
this._title = tabString[_index];
this._index = _index;
this.tabController.animateTo(_index);
});
},
items: <BottomNavigationBarItem>[
new BottomNavigationBarItem(
icon: new Icon(Icons.dashboard),
title: new Text("Discover"),
),
new BottomNavigationBarItem(
icon: new Icon(Icons.surround_sound),
title: new Text("Geners"),
),
new BottomNavigationBarItem(
icon: new Icon(Icons.account_circle),
title: new Text("Artists"),
),
]),
);
}
}
Check full code here
https://github.com/santoshanand/flutter_movie