I want to refresh the charts widget in every 5 seconds, But, here I am doing something wrong, but I do not know what I'm missing or what I did wrong. I only got the white screen in a view.
I have a dummy data in createRandomData
Calling the createRandomData
factory PieChartScreen.withRandomData(){
Timer.periodic(Duration(seconds: 5), (timer) {
return PieChartScreen(
createRandomData(),
);
});
}
Full SourceCode at PieChartScreen
import 'package:flutter/material.dart';
import 'package:charts_flutter/flutter.dart' as charts;
import 'dart:math';
import 'dart:async';
class PieChartScreen extends StatelessWidget {
List<charts.Series> seriesList;
final bool animate;
PieChartScreen(this.seriesList, {this.animate});
factory PieChartScreen.withSampleData(){
return new PieChartScreen(
createSampleData(),
animate: false,
);
}
factory PieChartScreen.withRandomData(){
Timer.periodic(Duration(seconds: 5), (timer) {
return PieChartScreen(
createRandomData(),
);
});
}
static List<charts.Series<Spending, int>> createRandomData() {
Timer.periodic(Duration(seconds: 5), (timer) {
final random = new Random();
final data = [
new Spending(2014, random.nextInt(1000000)),
new Spending(2015, random.nextInt(1000000)),
new Spending(2016, random.nextInt(1000000)),
new Spending(2017, random.nextInt(1000000)),
new Spending(2018, random.nextInt(1000000)),
new Spending(2019, random.nextInt(1000000)),
];
return [
new charts.Series(id: 'Spending',
data: data,
domainFn: (Spending sp, _) => sp.year,
measureFn: (Spending sp, _) => sp.spending,
labelAccessorFn: (Spending sp, _) => '${sp.year} : ${sp.spending}'
)
];
// Timer timer = new Timer.periodic(Duration(seconds: 2));
});
}
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: AppBar(
title: Text("Pie Chart Screen")
),
body: Padding(
padding: EdgeInsets.all(8.0),
child: new charts.PieChart(seriesList,
animate: animate,
defaultRenderer: new charts.ArcRendererConfig(
arcRendererDecorators: [
new charts.ArcLabelDecorator(
labelPosition: charts.ArcLabelPosition.auto)
]),
),
),
);
}
static List<charts.Series<Spending, int>> createSampleData() {
final data = [
new Spending(2014, 5),
new Spending(2014, 25),
new Spending(2014, 50),
new Spending(2014, 100),
new Spending(2014, 75),
new Spending(2014, 25),
];
return [
new charts.Series(id: 'Spending',
data: data,
domainFn: (Spending sp, _) => sp.year,
measureFn: (Spending sp, _) => sp.spending,
labelAccessorFn: (Spending sp, _) => '${sp.year} \n : ${sp.spending}'
)
];
}
}
class Spending {
final int year;
final int spending;
Spending(this.year, this.spending);
}
I hope the widget can be refreshed every 5 seconds.
I would change my PieChartScreen to a Stateful Widget.
class PieChartScreen extends StatelessWidget {
to something like this pseudo code:
class PieChartScreen extends StatefulWidget {
List<charts.Series> seriesList;
.....
int changeCounter=0;
.....
#override
PieChartScreenState createState() => PieChartScreenState();
}
class PieChartScreenState extends State<PieChartScreen> {
PieChartScreenState() {}
....
factory PieChartScreen.withRandomData(){
Timer.periodic(Duration(seconds: 5), (timer) {
return PieChartScreen(
setState (() {. // NOTE
createRandomData(),
changeCounter=changeCounter+1; // NOTE
}); // NOTE
);
});
}
....
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: AppBar(
title: Text("Pie Chart Screen")
),
body: changeCounter == 0 ? CircularProgressIndicator() : // NOTE
Padding(
padding: EdgeInsets.all(8.0),
child: new charts.PieChart(seriesList,
animate: animate,
defaultRenderer: new charts.ArcRendererConfig(
arcRendererDecorators: [
new charts.ArcLabelDecorator(
labelPosition: charts.ArcLabelPosition.auto)
]),
),
),
);
}
}
Notice the change of PieChartScreen to a StateFulWidget and the inclusion of the changeCounter variable which, in this case I just used an int for examples sake, is used in the setState construct to indicate a change in the state of the data which should be reflected in the Widget tree and in the Build method itself.
Related
I am using barcode_scan widget in my flutter app when I call Scan method this widget takes up the whole screen where it show the camera, I want to show that camera view inside another widget.
You can use package https://pub.dev/packages/last_qr_scanner or https://pub.dev/packages/qr_code_scanner
They both use platform view within Flutter
full example code of last_qr_scanner
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:last_qr_scanner/last_qr_scanner.dart';
void main() => runApp(MyApp());
class MyApp extends StatefulWidget {
const MyApp({
Key key,
}) : super(key: key);
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
final GlobalKey qrKey = GlobalKey(debugLabel: 'QR');
var qrText = "";
var controller;
#override
void initState() {
super.initState();
}
void _onQRViewCreated(QRViewController controller) {
this.controller = controller;
final channel = controller.channel;
controller.init(qrKey);
channel.setMethodCallHandler((MethodCall call) async {
switch (call.method) {
case "onRecognizeQR":
dynamic arguments = call.arguments;
setState(() {
qrText = arguments.toString();
});
}
});
}
#override
Widget build(BuildContext context) {
return new MaterialApp(
home: new Scaffold(
appBar: new AppBar(
title: new Text('Barcode Scanner Example'),
),
body: Column(
children: <Widget>[
Expanded(
child: LastQrScannerPreview(
key: qrKey,
onQRViewCreated: _onQRViewCreated,
),
flex: 4,
),
Expanded(
child: Text("This is the result of scan: $qrText"),
flex: 1,
),
Expanded(
child: RaisedButton(
onPressed: () {
this.controller.toggleTorch();
},
child: Text("Toggle Torch"),
),
flex: 1,
)
],
),
),
);
}
}
Your camera view must be a flutter widget to be embedded in another widget.
You can use this package which outputs the camera preview on a flutter texture and use the Mobile Vision API to detect QR codes and barcodes : https://github.com/rmtmckenzie/flutter_qr_mobile_vision
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.
in the Codelab English words example...
https://flutter.io/get-started/codelab/
The iOS Navigation transition is horizontal.. as you would expect a Segue to act in a UINavigationController. Right to left... Pops are left to right.
ANDROID, the same example is VERTICAL, Bottom to Top. Pops are Top to bottom.
MY QUESTION... how would I force a Horizontal transition in ANDROID so it behaves like iOS? I suspect I will have to use MaterialPageRoute
/*
Nguyen Duc Hoang(Mr)
Programming tutorial channel:
https://www.youtube.com/c/nguyenduchoang
Flutter, React, React Native, IOS development, Swift, Python, Angular
* */
import 'package:flutter/material.dart';
import 'package:english_words/english_words.dart';
//Define "root widget"
void main() => runApp(new MyApp());//one-line function
//StatefulWidget
class RandomEnglishWords extends StatefulWidget {
#override
State<StatefulWidget> createState() {
// TODO: implement createState
return new RandomEnglishWordsState();//return a state's object. Where is the state's class ?
}
}
//State
class RandomEnglishWordsState extends State<RandomEnglishWords>{
final _words = <WordPair>[];//Words displayed in ListView, 1 row contains 1 word
final _checkedWords = new Set<WordPair>();//set contains "no duplicate items"
#override
Widget build(BuildContext context) {
// TODO: implement build
//Now we replace this with a Scaffold widget which contains a ListView
return new Scaffold(
appBar: new AppBar(
title: new Text("List of English words"),
actions: <Widget>[
new IconButton(icon: new Icon(Icons.list),
onPressed: _pushToSavedWordsScreen)
],
),
body: new ListView.builder(itemBuilder: (context, index) {
//This is an anonymous function
//index = 0, 1, 2, 3,...
//This function return each Row = "a Widget"
if (index >= _words.length) {
_words.addAll(generateWordPairs().take(10));
}
return _buildRow(_words[index], index);//Where is _buildRow ?
}),
);
}
_pushToSavedWordsScreen() {
// print("You pressed to the right Icon");
//To navigate, you must have a "route"
final pageRoute = new MaterialPageRoute(builder: (context) {
//map function = Convert this list to another list(maybe different object's type)
//_checkedWords(list of WordPair) => map =>
// converted to a lazy list(Iterable) of ListTile
final listTiles = _checkedWords.map( (wordPair) {
return new ListTile(
title: new Text(wordPair.asUpperCase,
style: new TextStyle(fontSize: 20.0, fontWeight: FontWeight.bold),),
);
});
//Now return a widget, we choose "Scaffold"
return new Scaffold(
appBar: new AppBar(
title: new Text("Checked words"),
),
body: new ListView(children: listTiles.toList(),),//Lazy list(Iterable) => List
);
});
Navigator.of(context).push(pageRoute);
}
Widget _buildRow(WordPair wordPair, int index) {
//This widget is for each row
final textColor = index % 2 == 0 ? Colors.red : Colors.blue;
final isChecked = _checkedWords.contains(wordPair);
return new ListTile(
//leading = left, trailing = right. Is is correct ? Not yet
leading: new Icon(
isChecked ? Icons.check_box : Icons.check_box_outline_blank,
color: textColor,
),
title: new Text(
wordPair.asUpperCase,
style: new TextStyle(fontSize: 18.0, color: textColor),
),
onTap: () {
setState(() {
//This is an anonymous function
if (isChecked) {
_checkedWords.remove(wordPair);//Remove item in a Set
} else {
_checkedWords.add(wordPair);//Add item to a Set
}
});
},
);
}
}
class MyApp extends StatelessWidget {
//Stateless = immutable = cannot change object's properties
//Every UI components are widgets
#override
Widget build(BuildContext context) {
//build function returns a "Widget"
return new MaterialApp(
title: "This is my first Flutter App",
home: new RandomEnglishWords()
);//Widget with "Material design"
}
}
First of all about MaterialPageRoute does not help with your case. Here is the official explanation for it:
The MaterialPageRoute is handy because it transitions to the new
screen using a platform-specific animation.
And those animations you see are the platform-specific animations.
If you want to implement a custom animation, you need to implement it manually by using PageRouteBuilder. Here is how you can do it.
Here is a modified version of your _pushToSavedWordsScreen which does the right to left transition. Tested on Google Pixel.
final pageRoute = new PageRouteBuilder(
pageBuilder: (BuildContext context, Animation animation,
Animation secondaryAnimation) {
// YOUR WIDGET CODE HERE
final listTiles = _checkedWords.map((wordPair) {
return new ListTile(
title: new Text(
wordPair.asUpperCase,
style: new TextStyle(fontSize: 20.0, fontWeight: FontWeight.bold),
),
);
});
//Now return a widget, we choose "Scaffold"
return new Scaffold(
appBar: new AppBar(
title: new Text("Checked words"),
),
body: new ListView(
children: listTiles.toList(),
), //Lazy list(Iterable) => List
);
},
transitionsBuilder: (BuildContext context, Animation<double> animation,
Animation<double> secondaryAnimation, Widget child) {
return SlideTransition(
position: new Tween<Offset>(
begin: const Offset(1.0, 0.0),
end: Offset.zero,
).animate(animation),
child: new SlideTransition(
position: new Tween<Offset>(
begin: Offset.zero,
end: const Offset(1.0, 0.0),
).animate(secondaryAnimation),
child: child,
),
);
},
);
Navigator.of(context).push(pageRoute);
This is the modified code for the new Navigation. Android and iOS both Navigate Horizontally.
Origianl code: https://flutter.io/get-started/codelab/[1]
pubspec.yaml... Don't forget to add :
dependencies:
flutter:
sdk: flutter
english_words: ^3.1.0 #version 3.1.0 or above
UPDATED CODE: main.dart.
/*
Nguyen Duc Hoang(Mr)
Programming tutorial channel:
https://www.youtube.com/c/nguyenduchoang
Flutter, React, React Native, IOS development, Swift, Python, Angular
* */
import 'package:flutter/material.dart';
import 'package:english_words/english_words.dart';
//Define "root widget"
void main() => runApp(new MyApp());//one-line function
//StatefulWidget
class RandomEnglishWords extends StatefulWidget {
#override
State<StatefulWidget> createState() {
// TODO: implement createState
return new RandomEnglishWordsState();//return a state's object. Where is the state's class ?
}
}
//State
class RandomEnglishWordsState extends State<RandomEnglishWords> {
final _words = <WordPair>[
]; //Words displayed in ListView, 1 row contains 1 word
final _checkedWords = new Set<WordPair>(); //set contains "no duplicate items"
#override
Widget build(BuildContext context) {
// TODO: implement build
//Now we replace this with a Scaffold widget which contains a ListView
return new Scaffold(
appBar: new AppBar(
title: new Text("List of English words"),
actions: <Widget>[
new IconButton(icon: new Icon(Icons.list),
onPressed: _pushToSavedWordsScreen)
],
),
body: new ListView.builder(itemBuilder: (context, index) {
//This is an anonymous function
//index = 0, 1, 2, 3,...
//This function return each Row = "a Widget"
if (index >= _words.length) {
_words.addAll(generateWordPairs().take(10));
}
return _buildRow(_words[index], index); //Where is _buildRow ?
}),
);
}
_pushToSavedWordsScreen() {
// print("You pressed to the right Icon");
//To navigate, you must have a "route"
//======================================================================
//======= original solution - ANDROID transitions Vertically
// final pageRoute = new MaterialPageRoute(builder: (context) {
// //map function = Convert this list to another list(maybe different object's type)
// //_checkedWords(list of WordPair) => map =>
// // converted to a lazy list(Iterable) of ListTile
// final listTiles = _checkedWords.map( (wordPair) {
// return new ListTile(
// title: new Text(wordPair.asUpperCase,
// style: new TextStyle(fontSize: 20.0, fontWeight: FontWeight.bold),),
// );
// });
// //Now return a widget, we choose "Scaffold"
// return new Scaffold(
// appBar: new AppBar(
// title: new Text("Checked words"),
// ),
// body: new ListView(children: listTiles.toList(),),//Lazy list(Iterable) => List
// );
// });
// Navigator.of(context).push(pageRoute);
// }
//=========== OLD solution... ANDROID transitions Vertically
//==================================================================
//==================================================================
//=========== new solution... transition Horizontal
final pageRoute = new PageRouteBuilder(
pageBuilder: (BuildContext context, Animation animation,
Animation secondaryAnimation) {
// YOUR WIDGET CODE HERE
final listTiles = _checkedWords.map((wordPair) {
return new ListTile(
title: new Text(
wordPair.asUpperCase,
style: new TextStyle(fontSize: 20.0, fontWeight: FontWeight.bold),
),
);
});
//Now return a widget, we choose "Scaffold"
return new Scaffold(
appBar: new AppBar(
title: new Text("Checked words"),
),
body: new ListView(
children: listTiles.toList(),
), //Lazy list(Iterable) => List
);
},
transitionsBuilder: (BuildContext context, Animation<double> animation,
Animation<double> secondaryAnimation, Widget child) {
return SlideTransition(
position: new Tween<Offset>(
begin: const Offset(1.0, 0.0),
end: Offset.zero,
).animate(animation),
child: new SlideTransition(
position: new Tween<Offset>(
begin: Offset.zero,
end: const Offset(1.0, 0.0),
).animate(secondaryAnimation),
child: child,
),
);
},
);
Navigator.of(context).push(pageRoute);
}
//========= end of solution
//=============================================================
Widget _buildRow(WordPair wordPair, int index) {
//This widget is for each row
final textColor = index % 2 == 0 ? Colors.red : Colors.blue;
final isChecked = _checkedWords.contains(wordPair);
return new ListTile(
//leading = left, trailing = right. Is is correct ? Not yet
leading: new Icon(
isChecked ? Icons.check_box : Icons.check_box_outline_blank,
color: textColor,
),
title: new Text(
wordPair.asUpperCase,
style: new TextStyle(fontSize: 18.0, color: textColor),
),
onTap: () {
setState(() {
//This is an anonymous function
if (isChecked) {
_checkedWords.remove(wordPair); //Remove item in a Set
} else {
_checkedWords.add(wordPair); //Add item to a Set
}
});
},
);
}
}
class MyApp extends StatelessWidget {
//Stateless = immutable = cannot change object's properties
//Every UI components are widgets
#override
Widget build(BuildContext context) {
//build function returns a "Widget"
return new MaterialApp(
title: "This is my first Flutter App",
home: new RandomEnglishWords()
);//Widget with "Material design"
}
}
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;
});
}
}