I'm new to Flutter.
I have an app with 2 sub widgets (2 fragments in Android), and when i clicked next button in WidgetA, I want to replace (or push) that widget into WidgetChildA, like push (or replace) fragments in Android. But instead of that, I got a fullscreen widget like a normal screen in Flutter.
Here is my code:
import 'package:flutter/material.dart';
class DemoFragment extends StatefulWidget {
#override
State<StatefulWidget> createState() {
return new DemoFragmentState();
}
}
class DemoFragmentState extends State<DemoFragment> {
#override
Widget build(BuildContext context) {
print(context.toString() + context.hashCode.toString());
return new Scaffold(
appBar: new AppBar(title: new Text("Demo fragment")),
body: new Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
mainAxisSize: MainAxisSize.max,
children: <Widget>[
new FragmentA(),
new FragmentB()
],
),
);
}
}
class FragmentA extends StatelessWidget {
#override
Widget build(BuildContext context) {
print(context.toString() + context.hashCode.toString());
return new Center(
child: new Column(
children: <Widget>[
new Text("Fragment A"),
new RaisedButton(
child: new Text("next"),
onPressed: () {
print(context.toString() + context.hashCode.toString());
Navigator.of(context).push(new PageRouteBuilder(
opaque: true,
transitionDuration: const Duration(milliseconds: 0),
pageBuilder: (BuildContext context, _, __) {
return new FragmentChildA();
}));
/*showDialog(
context: context,
builder: (_) => new AlertDialog(
title: new Text("Hello world"),
content: new Text("this is my content"),
));*/
})
],
),
);
}
}
class FragmentB extends StatelessWidget {
#override
Widget build(BuildContext context) {
print(context.toString() + context.hashCode.toString());
return new Center(
child: new Column(
children: <Widget>[
new Text("Fragment B"),
new RaisedButton(
child: new Text("next"),
onPressed: () {
print(context.toString() + context.hashCode.toString());
Navigator.of(context).push(new PageRouteBuilder(
opaque: true,
transitionDuration: const Duration(milliseconds: 0),
pageBuilder: (BuildContext context, _, __) {
return new FragmentChildB();
}));
})
],
));
}
}
class FragmentChildA extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new Scaffold(
body: new Center(
child: new Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[new Text("Fragment Child A")],
)));
}
}
class FragmentChildB extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new Scaffold(
body: new Center(
child: new Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[new Text("Fragment Child B")],
)));
}
}
Screenshots:
Home page
After clicked
I'm not sure if you can use the router to replace just the part of a view; but you could conditionally change which Widget you render in the build method, like this:
children: <Widget>[
someCondition ? new FragmentA() : new FragmentChildA(),
new FragmentB()
],
Then you just need to set someCondition by using setState in the stateful widget:
setState(() => someCondition = true);
If you want to do this from inside FragmentA you could allow it to have the function passed into its constructor:
new FragmentA(
onPress: setState(() => someCondition = true)
)
However, it might be better to encapsulate all of this logic inside a single widget so this logic isn't all hanging around in the parent. You could make a single StatefulWidget for FragementA which keeps track of which stage you're on, and then in its build method renders the correct child widget, something like:
build() {
switch(stage) {
Stages.Stage1:
return new Stage1(
onNext: () => setState(() => stage = Stages.Stage2);
);
Stages.Stage2:
return new Stage1(
onPrevious: () => setState(() => stage = Stages.Stage1);
);
}
}
You could simply use a MaterialApp widget with the CupertinoPageTransitionsBuilder as pageTransitionTheme like
MaterialApp(
theme: ThemeData(
pageTransitionsTheme: PageTransitionsTheme(builders: {
TargetPlatform.iOS: CupertinoPageTransitionsBuilder(),
TargetPlatform.android: SlideRightPageTransitionsBuilder(),
}),
initialRoute: "fragment1",
routes: <String, WidgetBuilder>{
"fragment1": (BuildContext context) => Fragment1(),
"fragment2": (BuildContext context) => Fragment2(),
}
...
),
Then in fragment 1 you simply use the following to navigate to the other fragment with a slide animation
Navigator.of(context).pushReplacementNamed("fragment2");
Well, I found out the way to handle this case for a few months, but I just forgot to answer this question.
The solution is wrapping your Widget with a new Navigator.
You can see the video example here
And the simple demo for it here
The downside of this solution is sometimes, the keyboard is not showing as my intention.
ok I'm going to be doing this the same way google does it with the bottom navigation bar, I don't see this as the most performant but it works
class MainFabContainer extends StatefulWidget {
MainFabContainer({
Key key,
}) : super(key: key);
#override
State<StatefulWidget> createState() {
return MainFabContainerState();
}
}
class MainFabContainerState extends State<MainFabContainer> {
String title = "Title";
int _currentIndex = 0;
final List<int> _backstack = [0];
#override
Widget build(BuildContext context) {
//each fragment is just a widget which we pass the navigate function
List<Widget> _fragments =[Fragment1(navigate: navigateTo),Fragment2(navigate: navigateTo,),Fragment3(navigate: navigateTo,)];
//will pop scope catches the back button presses
return WillPopScope(
onWillPop: () {
customPop(context);
},
child: Scaffold(
drawer: drawer(),
appBar: AppBar(
title: Text(title),
),
body: Column(
children: <Widget>[
Expanded(
child: _fragments[_currentIndex],
),
],
),
),
);
}
void navigateTo(int index) {
_backstack.add(index);
setState(() {
_currentIndex = index;
});
}
void navigateBack(int index) {
setState(() {
_currentIndex = index;
});
}
customPop(BuildContext context) {
if (_backstack.length - 1 > 0) {
navigateBack(_backstack[_backstack.length - 1]);
} else {
_backstack.removeAt(_backstack.length - 1);
Navigator.pop(context);
}
}
//this method could be called by the navigate and navigate back methods
_setTitle(String appBarTitle) {
setState(() {
title = appBarTitle;
});
}
}
Related
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: HomePage(),
);
}
}
class HomePage extends StatefulWidget
{
#override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
bool valueOFCheck = false;
String opotion = "Option1";
String? dispalyedOption;
List? selctedOptions = ["1"];
List animal = ['Lion', 'Flamingo'];
List selectedValues = ['Dog', 'Arrow'];
List expectedValue = ['Flamingo', 'Lion'];
}
List value2 = [];
#override
Widget build(BuildContext context) {
var component;
var selectOption;
return Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Flexible(
child: MultiSelectDialogField(
buttonIcon: Icon(Icons.home),
items:
animal.map((e) => MultiSelectItem(e, e)).toList().cast(),
onConfirm: (value) {
if (ListEquality().equals(value.toList()..sort(),
expectedValue.toList()..sort())) {
} else {
value2.clear;
setState(() {
value = value2;
});
}
},
initialValue: [],
listType: MultiSelectListType.LIST,
onSelectionChanged: (value) {},
confirmText: const Text("Submit"),
cancelText: const Text('cancel'),
buttonText: Text("HI"),
),
),
//2728
Text(selectedValues.join() ?? "delete"),
textFunction(onCancel: () {
setState(() {
selectedValues.clear();
});
})
]),
),
);
}
Widget textFunction({Function? onCancel}) {
return ElevatedButton(
onPressed: () {
onCancel!();
},
child: Text("TextButton"));
}
}
if click text button the value display in multiselect must be disappear
value set in setState will trigger and clear value
but i need the setState must be trigger when i click to the textbutton
i call the setstate using function but it will not work
packge for the multiselect is
multi_select_fluttermulti_select_flutter.dart
In the code below I am trying to build a basic ToDo list app using flutter. I have a FAB and when it is pressed, it asks the user to enter a text in the popped up alert dialog that contains a TextField. I also use a TextEditingController to get the text and add it to a list of strings.
I have a counter variable to keep track of items being added to the list and to use it as index of the list when I want to show the item from the list and add it to the ListView as a ListTile.
When I run the code it says the index is out of range and I really don't know what else should I take care of. Sorry if my question is basic, I am newbie.
My Code:
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: 'ToDo List',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyTaskList(),
);
}
}
class MyTaskList extends StatefulWidget {
#override
_MyTaskListState createState() => _MyTaskListState();
}
class _MyTaskListState extends State<MyTaskList> {
final _taskItems = <String>[];
var _counter = 0;
final myController = TextEditingController();
#override
void dispose(){
myController.dispose();
super.dispose();
}
void _addTask(String task){
setState(() {
_taskItems.add(task);
});
myController.clear();
}
Widget _buildTaskList() {
return new ListView.builder(
itemCount: _taskItems.length,
itemBuilder: (BuildContext context, _) {
print(_taskItems);
return _buildTask(_taskItems[_counter]);
}
);
}
Widget _buildTask(String task) {
return new ListTile(
title: new Text(task),
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("ToDo List"),
),
floatingActionButton: FloatingActionButton(
onPressed: () => showDialog<String>(
context: context,
builder: (context) => AlertDialog(
title: Text("New Task"),
content: TextField(
controller: myController,
decoration: InputDecoration(
border: OutlineInputBorder(),
hintText: "Enter New Task",
),
),
actions: <Widget>[
TextButton(
onPressed: () => {
_addTask(myController.text),
Navigator.pop(context, "ok"),
_counter++,
print(_counter),
print(_taskItems),
},
child: const Text("OK")),
],
)
),
child: Center(child:Icon(Icons.add)),
),
body: _buildTaskList(),
);
}
}
Edit as below. You can use the ListViewBuilder index, why do you use counter? I think, your initial counter value is 0 but the list is empty. You try to get element 0 (or first)` of empty list, but there is no element.
Widget _buildTaskList() {
return new ListView.builder(
itemCount: _taskItems.length,
itemBuilder: (BuildContext context, index) {
print(_taskItems);
return _buildTask(_taskItems[index]);
}
);
}
I'm trying to implement an automatic Search Bar in Flutter.
When compiling I get the error: The argument type 'SearchBar' can't be assigned to the parameter type 'Widget?'.
class Home extends StatelessWidget {
Future<List<Post>> search(String search) async {
await Future.delayed(Duration(seconds: 2));
return List.generate(search.length, (int index) {
return Post(
"Title : $search $index",
"Description :$search $index",
);
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 20),
child: SearchBar<Post>(
onSearch: search,
onItemFound: (Post post, int index) {
return ListTile(
title: Text(post.title),
subtitle: Text(post.description),
);
},
),
),
),
);
}
}
From the official Documentation, you need to call the build method to get a widget.
However, it would be better if you create your SearchBar inside your constructor itself, so that a new one doesn't get created every time.
From the official documentation,
class _MyHomePageState extends State<MyHomePage> {
SearchBar searchBar;
AppBar buildAppBar(BuildContext context) {
return new AppBar(
title: new Text('My Home Page'),
actions: [searchBar.getSearchAction(context)]
);
}
_MyHomePageState() {
searchBar = new SearchBar(
inBar: false,
setState: setState,
onSubmitted: print,
buildDefaultAppBar: buildAppBar
);
}
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: searchBar.build(context)
);
}
}
I've been looking for answers for a whole day, I'm tryin to add fields in a form dynamically (for the moment, by creating a loop which create fields), I can't find any answer for my problem. If you can do something, just let me know :
I'm creating a view with my Fieldset class, which is my form and I put a Field list inside :
List<Field> fieldset;
void main() {
for (int i = 0; i < 10; i++) {
Field field = Field();
fieldset.add(field);
};
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
final appTitle = 'Form Validation Demo';
return MaterialApp(
title: appTitle,
home: Scaffold(
appBar: AppBar(
title: Text(appTitle),
),
body: Fieldset(fieldset: List<Field>()),
),
);
}
}
As you can see, I'm just launching the app, creating some fields with the loop and putting it in Fieldset Widget go ahead :
class Fieldset extends StatefulWidget {
final List<Field> fieldset;
final state = _FieldsetState();
#override
_ FieldsetState createState() {
return _FieldsetState();
}
Fieldset({this.fieldset});
}
class _FieldsetState extends State<Fieldset> {
final _formKey = GlobalKey<FormState>();
#override
Widget build(BuildContext context) {
return Form(
key: _formKey,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget> [
// widget.fieldset.forEach((f) => f.build(context)) ==> THIS DOESN'T WORK
,
RaisedButton(
onPressed: () {
if (_formKey.currentState.validate()) {
Scaffold.of(context)
.showSnackBar(SnackBar(content: Text('processing')));
}
},
child: Text('Submit'),
)
]));
}
}
I really hope you can help me, Looks like i've missed something..
try this
var listOfFields = <Widget>[];
listOfFields.add(RaisedButton( onPressed : (){
addNewField();
}, child: Text("Add new Field")
));
void addNewField(){
setState((){
listOfFields.add(TextFormField());
});
}
final _formKey = GlobalKey<FormState>()
#override
Widget build(BuildContext context) {
return Form(
key : _formKey,
child :Scaffold(
body : ListView.builder(
itemCount: listOfFields.length,
itemBuilder: (context, index){
return listOfFields[index];
}
)
)
);
}
I am unable to toggle the camera. Can someone help me with the same.
"onPressed: () => _cameraSwitcher()" call is being made but still I am getting below error.
I/flutter (19390): The following NoSuchMethodError was thrown while handling a gesture:
I/flutter (19390): The method 'toggleCamera' was called on null.
I/flutter (19390): Receiver: null
Below is detailed code.
util.dart
import "dart:async";
import "package:flutter/material.dart";
import "package:camera/camera.dart";
class CameraWidget extends StatefulWidget {
static CameraWidgetState of(BuildContext context) =>
context.ancestorStateOfType(const TypeMatcher<CameraWidgetState>());
#override
CameraWidgetState createState() => CameraWidgetState();
}
class CameraWidgetState extends State<CameraWidget> {
List<CameraDescription> cameras;
CameraController controller;
bool isReady= false;
int selectedCamera = 1;
bool noCameraDevice = false;
#override
void initState() {
super.initState();
_setupCamera();
}
Future<void> _setupCamera() async {
try {
// initialize cameras.
cameras = await availableCameras();
// initialize camera controllers.
controller = new CameraController(
cameras[selectedCamera], ResolutionPreset.medium
);
await controller.initialize();
} on CameraException catch (_) {
//debugPrint("Some error occured!").
}
if (!mounted) return;
setState(() {
isReady = true;
});
}
#override
Widget build(BuildContext context) {
if (controller == null || !controller.value.isInitialized) {
return new Container();
}
return new AspectRatio(
aspectRatio: controller.value.aspectRatio,
child: new CameraPreview(controller));
}
void toggleCamera(){
setState(() {
selectedCamera= (selectedCamera==1)? 0: 1;
});
}
}
mirror.dart
//import "dart:async";
import "package:flutter/material.dart";
import "../core/util.dart";
class MirrorApp extends StatefulWidget {
#override
_MirrorAppState createState() => _MirrorAppState();
}
class _MirrorAppState extends State<MirrorApp> {
GlobalKey<CameraWidgetState> _cameraWidgetStateKey = new GlobalKey<CameraWidgetState>();
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: Text("Will show ads here"),
backgroundColor: Colors.blue
),
body: new Stack(
children: <Widget>[
new Center(
child: new CameraWidget(),
),
new Row(
children: <Widget>[
new IconButton(
icon: new Icon(Icons.camera_front),
onPressed: () => _cameraSwitcher()
),
new IconButton(
icon: new Icon(Icons.photo_camera),
onPressed: () => _photoClicked()
),
new IconButton(
icon: new Icon(Icons.photo),
onPressed: () => _photoGallery()
),
new IconButton(
icon: new Icon(Icons.share),
onPressed: () => _shareOnSocialMedia()
)
],
)
],
),
);
}
_cameraSwitcher() {
debugPrint("Clicked Toggle camera!!");
// int selectedCamera = _cameraWidgetStateKey.currentState.selectedCamera;
_cameraWidgetStateKey.currentState.toggleCamera();// = 0; // toggleCamera();
}
_photoClicked() { return debugPrint("Photo clicked!!");}
_photoGallery() { return debugPrint("Open Photo gallery!!");}
_shareOnSocialMedia() { return debugPrint("Share on social media");}
}
EDIT 1-
After following Yamin suggestion I have to update toggleCamera method as below to actually toggle camera.
void toggleCamera(){
setState(() {
debugPrint("toggler called $selectedCamera");
selectedCamera= (selectedCamera==1)? 0: 1;
debugPrint("toggler called $selectedCamera");
_setupCamera();
});
}
But I would like to know if this is correct way? I was under impression that if i will update selectedCamera variable its enough to rebuild the Camera widget. Is this due to future object?
The globalKey is created but it's not assigned to the widget. So it's null when you call it:
In util.dart
class CameraWidget extends StatefulWidget {
static CameraWidgetState of(BuildContext context) =>
context.ancestorStateOfType(const TypeMatcher<CameraWidgetState>());
CameraWidget({Key key}) : super(key: key); //This line is added
#override
CameraWidgetState createState() => CameraWidgetState();
}
And in mirror.dart line 29:
body: new Stack(
children: <Widget>[
new Center(
child: new CameraWidget(
key: _cameraWidgetStateKey, //this line is added
),
),
It's good to check the globalKey docs and this question
UPDATE: As I know, there is nothing wrong with the updated code and if it works without any issue, it's good.