I want to use data parsed with if statement before override,
how to do that?
I tried many way but didn't worked.
please help me.
import 'package:flutter/material.dart';
class MyButton extends StatefulWidget {
Function buttonFunction;
String buttonName;
MyButton(this.buttonFunction,this.buttonName);
#override
_MyButtonState createState() => _MyButtonState();
}
class _MyButtonState extends State<MyButton> {
if (widget.buttonName == "ok"){
int i =1;
}
else int i =2;
#override
Widget build(BuildContext context) {
return Container(
child: RaisedButton(
onPressed: () {
widget.buttonFunction();
},
child: Text("${widget.buttonName}",
),
),
);
}
}```
You should do most of your initializings in the initState method.
int i;
#override
void initState() {
if (widget.buttonName == "ok") {
i = 1;
} else
i = 2;
super.initState();
}
Related
Okay. So I'm going to show some code, and I honestly don't know WHY it doesn't work. I just feel like I'm out of my depth, and this is very frustrating.
Now this is NOT the program I'm actually working on, but a super-simple example program that should show the issue I'm having. Please do NOT ask me to put all of these things into or inside a single function or class, as that is NOT an option with my real program, so it wouldn't solve my actual issue.
so in my main.dart I have the following.
import 'package:flutter/cupertino.dart';
import 'dart:async';
import './page2.dart';
void main() => runApp(MyApp());
Page2 myPage = new Page2();
PageState myState = myPage.createState();
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return CupertinoApp(
title: 'Splash Test',
theme: CupertinoThemeData(
primaryColor: Color.fromARGB(255, 0, 0, 255),
),
home: MyHomePage(title: 'Splash Test 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> {
bool textBool = false;
void changeTest(dynamic function, context) async {
Timer.periodic(Duration (seconds: 2), (Timer t) {
myState.changeText();
counter++;
if (counter >= 10) {
t.cancel();
}
},);
Navigator.push(context, CupertinoPageRoute(builder: (context) => myPage));
}
#override
Widget build(BuildContext context) {
return CupertinoPageScaffold(
child: Center(
child: CupertinoButton(
child: Text('To Splash'),
onPressed: () => changeTest(myState.changeText, context),
),
),
);
}
}
and in a second Dart file I have
import 'package:flutter/material.dart';
import 'package:flutter/semantics.dart';
import './main.dart';
class Page2 extends StatefulWidget {
#override
State<StatefulWidget> createState() => new PageState();
}
class PageState extends State<Page2> {
bool textChanger = false;
bool firstText = true;
Text myText() {
if (textChanger) {
Text text1 = new Text('Text One',
style: TextStyle(color: Color.fromARGB(255, 0, 0, 0)));
return text1;
} else {
Text text1 = new Text('Text Two',
style: TextStyle(color: Color.fromARGB(255, 0, 0, 0)));
return text1;
}
}
void changeText() {
if (!firstText) {
if (textChanger) {
print('Change One');
textChanger = false;
setState(() {
});
} else {
print('Change Two');
textChanger = true;
setState(() {
});
}
} else {
firstText = false;
}
}
#override
Widget build(BuildContext context) {
return new Scaffold(
body: new Container(
child: Center(
child: myText()
)
),);
}
}
Now what this program does is switch to the second page, and then stalls, and nothing happens. The timer IS getting called (I can see this through the print-screen function) And I can see that the text SHOULD be changing, as the bools are being altered properly to do so.
Expected functionality: I should be able to call the instance of the second page, and the functions on it, from my main app, and make changes to the text on that second page.
In my real app (Far more complicated, I couldn't possibly parse it down into something that would fit here) I have the same issue. (If I use the hot reload in Flutter the text DOES change in my actual app.)
So as you can see, I'm trying to communicate cross-classes and cross-functions, but either A) I'm not communicating correctly, or B) The communication is with an incorrect instance of the secondary page, and so the setState() call isn't being done on the variant that's being shown? Those are my only guesses.
You shouldn't call the createState manually. For implementing such a thing I prefer to use a stream instead, which is pretty much easy to handle.
timerStream.dart
import 'dart:async';
class TimerStream {
StreamController _streamController;
StreamSink<bool> get timerSink =>
_streamController.sink;
Stream<bool> get timerStream =>
_streamController.stream;
TimerStream() {
_streamController = StreamController<bool>();
}
dispose() {
_streamController?.close();
}
}
main.dart
import 'dart:async';
import 'package:flutter/cupertino.dart';
import './page2.dart';
import './timerStream.dart';
void main() => runApp(MyApp());
TimerStream stream = TimerStream();
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return CupertinoApp(
title: 'Splash Test',
theme: CupertinoThemeData(
primaryColor: Color.fromARGB(255, 0, 0, 255),
),
home: MyHomePage(title: 'Splash Test 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> {
bool textBool = false;
void changeTest(context) async {
Navigator.push(context, CupertinoPageRoute(builder: (context) => Page2(stream: stream,)));
Timer.periodic(Duration (seconds: 5), (Timer t) {
stream.timerSink.add(true);
});
}
#override
Widget build(BuildContext context) {
return CupertinoPageScaffold(
child: Center(
child: CupertinoButton(
child: Text('To Splash'),
onPressed: () => changeTest(context),
),
),
);
}
}
page2.dart
import 'package:flutter/material.dart';
import 'timerStream.dart';
class Page2 extends StatefulWidget {
TimerStream stream;
Page2({this.stream});
#override
State<StatefulWidget> createState() => new PageState();
}
class PageState extends State<Page2> {
bool textChanger = false;
bool firstText = true;
Text myText() {
if (textChanger) {
Text text1 = new Text('Text One',
style: TextStyle(color: Color.fromARGB(255, 0, 0, 0)));
return text1;
} else {
Text text1 = new Text('Text Two',
style: TextStyle(color: Color.fromARGB(255, 0, 0, 0)));
return text1;
}
}
void changeText() {
if (!firstText) {
if (textChanger) {
print('Change One');
setState(() {
textChanger = false;
});
} else {
print('Change Two');
setState(() {
textChanger = true;
});
}
} else {
firstText = false;
}
}
#override
Widget build(BuildContext context) {
return new Scaffold(
body: new Container(
child: Center(
child: myText()
)
),);
}
#override
void initState() {
super.initState();
widget.stream.timerStream.listen((onData) {
changeText();
});
}
}
Note: If you want, instead of writing true to the stream you can toggle the value and use that in your page2 to change the text.
I have made a simple app using bloc and InheritedWidget.
Following is the code
class Bloc {
final StreamController<bool> _changeColor = PublishSubject<bool>();
Function(bool) get changeColour => _changeColor.sink.add;
Stream<bool> get colour => _changeColor.stream;
void dispose(){
_changeColor.close();
}
}
class Provider extends InheritedWidget {
final bloc = Bloc();
Provider({Key key,Widget child}): super(key: key,child: child);
#override
bool updateShouldNotify(InheritedWidget oldWidget) {
return true;
}
static Bloc of(BuildContext context){
return (context.inheritFromWidgetOfExactType(Provider) as Provider).bloc;
}
void dispose(){
bloc?.dispose();
}
}
class _HomePageState extends State<HomePage> {
var bloc;
#override
void initState() {
super.initState();
bloc = Provider.of(context);
}
#override
Widget build(BuildContext context) {
return Column(
children: <Widget>[
RaisedButton(
onPressed: () {
bloc.changeColour(true);
},
child: Text("Change colour"),
),
StreamBuilder(
builder: (context, snapshot) {
var bool = snapshot?.data ?? false;
return Text(
"First text",
style:
TextStyle(color: bool ? Colors.red : Colors.green),
);
},
stream: bloc?.colour,
),
],
);
}
#override
void dispose() {
super.dispose();
}
}
I don't understand how to call dispose method of the bloc when using InheritedWidget. Of course I can create a global variable of bloc and avoid using InheritedWidget to dispose the bloc using the dispose method which is present in the bloc but I really want to use InheritedWidget.
Does using the PublishSubject from rxdart disposes the streamcontroller automatically, is it life cycle aware, I couldn't find anything related to this in the documentation. Is there any debugging process to make sure the streamcontroller is disposed off correctly?
That is not possible using Inheritedwidget. The widget is not made to handle data, but to share it.
You have to wrap your Inheritedwidget into a StatefulWidget and use the dispose of the latter
To add to Remi's answer, the code would look something like this
import 'package:flutter/material.dart';
abstract class BlocBase {
void dispose();
}
class BlocProvider<T extends BlocBase> extends StatefulWidget {
BlocProvider({
Key key,
#required this.child,
#required this.bloc,
}): super(key: key);
final T bloc;
final Widget child;
#override
_BlocProviderState<T> createState() => _BlocProviderState<T>();
static T of<T extends BlocBase>(BuildContext context){
final type = _typeOf<BlocProvider<T>>();
BlocProvider<T> provider = context.ancestorWidgetOfExactType(type);
return provider.bloc;
}
static Type _typeOf<T>() => T;
}
class _BlocProviderState<T> extends State<BlocProvider<BlocBase>>{
#override
void dispose(){
widget.bloc.dispose();
super.dispose();
}
#override
Widget build(BuildContext context){
return widget.child;
}
}
class Bloc implements BlocBase {
final StreamController<bool> _changeColor = PublishSubject<bool>();
Function(bool) get changeColour => _changeColor.sink.add;
Stream<bool> get colour => _changeColor.stream;
#override
void dispose() {
_changeColor.close();
}
}
class _HomePageState extends State<HomePage> {
Bloc bloc;
var colour = false;
#override
void initState() {
super.initState();
bloc = BlocProvider.of<Bloc>(context);
}
#override
Widget build(BuildContext context) {
return Column(
children: <Widget>[
RaisedButton(
onPressed: () {
if (colour) {
bloc.changeColour(false);
colour = false;
} else {
bloc.changeColour(true);
colour = true;
}
},
child: Text("Change colour"),
),
StreamBuilder(
builder: (context, snapshot) {
var bool = snapshot?.data ?? false;
return Text(
"First text",
style: TextStyle(color: bool ? Colors.red : Colors.green),
);
},
stream: bloc?.colour,
),
],
);
}
#override
void dispose() {
print("Bloc is disposed");
bloc.dispose();
super.dispose();
}
}
Just testing out flutter. The code sample below is a very simple flutter app. The problem is that I don't know how to call the setState() function inside the TestTextState class in order to change the text each time when the change button is pressed.
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: 'Test app',
home: new Scaffold(
appBar: new AppBar(
title: new Text("Test"),
),
body: new Test(),
),
);
}
}
class Test extends StatelessWidget {
final TestText testText = new TestText();
void change() {
testText.text == "original" ? testText.set("changed") : testText.set("original");
}
#override
Widget build(BuildContext context) {
return new Column(
children: [
testText,
new RaisedButton(
child: new Text("change"),
onPressed: () => change(),
),
]
);
}
}
class TestText extends StatefulWidget {
String text = "original";
void set(String str) {
this.text = str;
}
#override
TestTextState createState() => new TestTextState();
}
class TestTextState extends State<TestText> {
#override
Widget build(BuildContext context) {
return new Text(this.widget.text);
}
}
I have approached this problem by initializing the _TestTextState as the final property of the TestText widget which allows to simply update the state when the change button is pressed. It seems like a simple solution but I'm not sure whether it's a good practice.
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: 'Test app',
home: new Scaffold(
appBar: new AppBar(
title: new Text("Test"),
),
body: new Test(),
),
);
}
}
class Test extends StatelessWidget {
final _TestText text = new _TestText();
#override
Widget build(BuildContext context) {
return new Column(
children: [
text,
new RaisedButton(
child: new Text("change"),
onPressed: () => text.update(),
),
]
);
}
}
class TestText extends StatefulWidget {
final _TestTextState state = new _TestTextState();
void update() {
state.change();
}
#override
_TestTextState createState() => state;
}
class _TestTextState extends State<TestText> {
String text = "original";
void change() {
setState(() {
this.text = this.text == "original" ? "changed" : "original";
});
}
#override
Widget build(BuildContext context) {
return new Text(this.text);
}
}
thier is no way to do so. any how you have to convert your StatelessWidget to StatefulWidget.
Solution based on your existing code
class Test extends StatelessWidget {
final StreamController<String> streamController = StreamController<String>.broadcast();
#override
Widget build(BuildContext context) {
final TestText testText = TestText(streamController.stream);
return new Column(children: [
testText,
new RaisedButton(
child: Text("change"),
onPressed: () {
String text = testText.text == "original" ? "changed" : "original";
streamController.add(text);
},
),
]);
}
}
class TestText extends StatefulWidget {
TestText(this.stream);
final Stream<String> stream;
String text = "original";
#override
TestTextState createState() => new TestTextState();
}
class TestTextState extends State<TestText> {
#override
void initState() {
widget.stream.listen((str) {
setState(() {
widget.text = str;
});
});
super.initState();
}
#override
Widget build(BuildContext context) {
return Text(widget.text);
}
}
But it's not the best idea - to use non-final field inside Stateful Widget
P.S.
You can also use this - scoped_model
I have made a grid of buttons using flutter but now I want to swipe through 2 or more buttons in a single drag such that all the buttons through which I am dragging gets selected.
I have checked out some questions on the same and I was redirected to use gesture detector but that's not enough. I need certain properties or better a sample code such that I am able to work through it.
an example of the dragable app is http://a5.mzstatic.com/us/r30/Purple60/v4/6f/00/35/6f0035d3-1bab-fcbb-cb13-8ab46cf3c44d/screen696x696.jpeg
You can manually hit test RenderBox and extract a specific RenderObject of your choice.
We could for example add the following renderobject above our buttons:
class Foo extends SingleChildRenderObjectWidget {
final int index;
Foo({Widget child, this.index, Key key}) : super(child: child, key: key);
#override
RenderObject createRenderObject(BuildContext context) {
return _Foo()..index = index;
}
#override
void updateRenderObject(BuildContext context, _Foo renderObject) {
renderObject..index = index;
}
}
class _Foo extends RenderProxyBox {
int index;
}
Then use a Listener to extract all _Foo found under the pointer.
Here's a full application using this principle:
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(primarySwatch: Colors.blue),
home: Grid(),
);
}
}
class Grid extends StatefulWidget {
#override
GridState createState() {
return new GridState();
}
}
class GridState extends State<Grid> {
final Set<int> selectedIndexes = Set<int>();
final key = GlobalKey();
final Set<_Foo> _trackTaped = Set<_Foo>();
_detectTapedItem(PointerEvent event) {
final RenderBox box = key.currentContext.findRenderObject();
final result = BoxHitTestResult();
Offset local = box.globalToLocal(event.position);
if (box.hitTest(result, position: local)) {
for (final hit in result.path) {
/// temporary variable so that the [is] allows access of [index]
final target = hit.target;
if (target is _Foo && !_trackTaped.contains(target)) {
_trackTaped.add(target);
_selectIndex(target.index);
}
}
}
}
_selectIndex(int index) {
setState(() {
selectedIndexes.add(index);
});
}
#override
Widget build(BuildContext context) {
return Listener(
onPointerDown: _detectTapedItem,
onPointerMove: _detectTapedItem,
onPointerUp: _clearSelection,
child: GridView.builder(
key: key,
itemCount: 6,
physics: NeverScrollableScrollPhysics(),
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3,
childAspectRatio: 1.0,
crossAxisSpacing: 5.0,
mainAxisSpacing: 5.0,
),
itemBuilder: (context, index) {
return Foo(
index: index,
child: Container(
color: selectedIndexes.contains(index) ? Colors.red : Colors.blue,
),
);
},
),
);
}
void _clearSelection(PointerUpEvent event) {
_trackTaped.clear();
setState(() {
selectedIndexes.clear();
});
}
}
class Foo extends SingleChildRenderObjectWidget {
final int index;
Foo({Widget child, this.index, Key key}) : super(child: child, key: key);
#override
_Foo createRenderObject(BuildContext context) {
return _Foo()..index = index;
}
#override
void updateRenderObject(BuildContext context, _Foo renderObject) {
renderObject..index = index;
}
}
class _Foo extends RenderProxyBox {
int index;
}
I don't like this code at all, but it seems to be working
import 'package:flutter/material.dart';
class TestScaffold extends StatefulWidget {
#override
State<StatefulWidget> createState() => _TestScaffoldState();
}
List<_SquareButton> _selectedList = [];
class _TestScaffoldState extends State<TestScaffold> {
List<_SquareButton> buttons = [
_SquareButton('1'),
_SquareButton('2'),
_SquareButton('3'),
_SquareButton('4'),
_SquareButton('5'),
_SquareButton('6'),
_SquareButton('7'),
_SquareButton('8'),
_SquareButton('9'),
_SquareButton('10'),
_SquareButton('11'),
_SquareButton('12'),
_SquareButton('13'),
_SquareButton('14'),
_SquareButton('15'),
_SquareButton('16'),
];
Map<Rect, _SquareButton> positions = {};
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Test'),),
body: GestureDetector(
onPanDown: (details) {
checkGesture(details.globalPosition);
},
onPanUpdate: (details) {
checkGesture(details.globalPosition);
},
child: GridView.count(crossAxisCount: 4,
physics: NeverScrollableScrollPhysics(),
children: buttons,),)
);
}
initPositions() {
if (positions.isNotEmpty) return;
buttons.forEach((btn) {
RenderBox box = btn.bKey.currentContext.findRenderObject();
Offset start = box.localToGlobal(Offset.zero);
Rect rect = Rect.fromLTWH(start.dx, start.dy, box.size.width, box.size.height);
positions.addAll({rect: btn});
});
}
checkGesture(Offset position) {
initPositions();
positions.forEach((rect, btn) {
if (rect.contains(position)) {
if (!_selectedList.contains(btn)) {
_selectedList.add(btn);
btn.state.setState((){});
}
}
});
}
}
class _SquareButton extends StatefulWidget {
_SquareButton(this.title);
final String title;
final GlobalKey bKey = GlobalKey();
State state;
#override
State<StatefulWidget> createState() {
state = _SquareButtonState();
return state;
}
}
class _SquareButtonState extends State<_SquareButton> {
#override
Widget build(BuildContext context) {
return Padding(key: widget.bKey, padding: EdgeInsets.all(4.0), child: Container(
color: _selectedList.contains(widget) ? Colors.tealAccent : Colors.teal,
child: Text(widget.title),
alignment: Alignment.center,
),);
}
}
There is a moment.
If you enable scrolling - GestureDetector not always work on vertical movements
I am using imagecarousel package for displaying images from the network. I want to keep onPressed function for images in the slide.
new ImageCarousel(
<ImageProvider>[
new NetworkImage('http://www.hilversum.ferraridealers.com/siteasset/ferraridealer/54f07ac8c35b6/961/420/selected/0/0/0/54f07ac8c35b6.jpg'),
new NetworkImage('http://auto.ferrari.com/en_EN/wp-content/uploads/sites/5/2017/08/ferrari-portofino-reveal-2017-featured-new.jpg'),
new NetworkImage('http://www.hilversum.ferraridealers.com/siteasset/ferraridealer/54f07ac8c35b6/961/420/selected/0/0/0/54f07ac8c35b6.jpg'),
],
interval: new Duration(seconds: 1),
)
After making some modifications to Image Carousel, I was able to implement click event (other events also possible). Here is the sample code.
import 'dart:async';
import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
class ImageCarousel extends StatefulWidget {
final List<ImageProvider> imageProviders;
final double height;
final TargetPlatform platform;
final Duration interval;
final TabController tabController;
final BoxFit fit;
// Images will shrink according to the value of [height]
// If you prefer to use the Material or Cupertino style activity indicator set the [platform] parameter
// Set [interval] to let the carousel loop through each photo automatically
// Pinch to zoom will be turned on by default
ImageCarousel(this.imageProviders,
{this.height = 250.0, this.platform, this.interval, this.tabController, this.fit = BoxFit.cover});
#override
State createState() => new _ImageCarouselState();
}
TabController _tabController;
class _ImageCarouselState extends State<ImageCarousel> with SingleTickerProviderStateMixin {
#override
void initState() {
super.initState();
_tabController = widget.tabController ?? new TabController(vsync: this, length: widget.imageProviders.length);
if (widget.interval != null) {
new Timer.periodic(widget.interval, (_) {
_tabController.animateTo(_tabController.index == _tabController.length - 1 ? 0 : ++_tabController.index);
});
}
}
#override
void dispose() {
_tabController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return new SizedBox(
height: widget.height,
child: new TabBarView(
controller: _tabController,
children: widget.imageProviders.map((ImageProvider provider) {
return new CarouselImageWidget(widget, provider, widget.fit, widget.height);
}).toList(),
),
);
}
}
class CarouselImageWidget extends StatefulWidget {
final ImageCarousel carousel;
final ImageProvider imageProvider;
final BoxFit fit;
final double height;
CarouselImageWidget(this.carousel, this.imageProvider, this.fit, this.height);
#override
State createState() => new _CarouselImageState();
}
class _CarouselImageState extends State<CarouselImageWidget> {
bool _loading = true;
Widget _getIndicator(TargetPlatform platform) {
if (platform == TargetPlatform.iOS) {
return new CupertinoActivityIndicator();
} else {
return new Container(
height: 40.0,
width: 40.0,
child: new CircularProgressIndicator(),
);
}
}
#override
void initState() {
super.initState();
widget.imageProvider.resolve(new ImageConfiguration()).addListener((i, b) {
if (mounted) {
setState(() {
_loading = false;
});
}
});
}
#override
Widget build(BuildContext context) {
return new Container(
height: widget.height,
child: _loading
? _getIndicator(widget.carousel.platform == null ? defaultTargetPlatform : widget.carousel.platform)
: new GestureDetector(
child: new Image(
image: widget.imageProvider,
fit: widget.fit,
),
onTap: () {
int index = int.parse(_tabController.index.toString());
switch(index){
//Implement you case here
case 0:
case 1:
case 2:
default:
print(_tabController.index.toString());
}
},
),
);
}
}
void main(){
runApp(new MaterialApp(
home: new Scaffold(
appBar: new AppBar(
title: new Text("Demo"),
),
body: new ImageCarousel(
<ImageProvider>[
new NetworkImage(
'http://wallpaper-gallery.net/images/images/images-2.jpg'),
new NetworkImage(
'http://wallpaper-gallery.net/images/images/images-10.jpg'),
new NetworkImage(
'http://wallpaper-gallery.net/images/images/images-4.jpg'),
],
interval: new Duration(seconds: 5),
)
),
));
}
Hope it helps..!!