CustomPaint designs one below another in Flutter - flutter

I am trying to build a website using flutter and I want it's background to have a wavy corner with a different color at one end. I need four blocks in this designs i.e. as you scroll you'll reveal new blocks, and the height of each block should be the same as the height of the screen.
So, I thought of using CustomPaint to do it so I started to implement it but I am getting around 500 lines of error messages and it's not working.
I'm new to flutter so I don't know if I've implemented everything the way it should be implemented. I've shared all the code down below:
main.dart
import 'package:flutter/material.dart';
import 'blocks/block1.dart';
import 'blocks/block2.dart';
import 'blocks/block3.dart';
import 'blocks/block4.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: Home(),
);
}
}
class Home extends StatefulWidget {
#override
_HomeState createState() => _HomeState();
}
class _HomeState extends State<Home> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: SingleChildScrollView(
child: Column(
children: <Widget>[
BlockOne(),
//BlockTwo(),
//BlockThree(),
//BlockFour()
],
),
)
);
}
}
block1.dart - block 2, 3 and 4 are almost similar:
Note- I've not added the wavy pattern in this code below because this itself is not working
import 'package:flutter/material.dart';
class BlockOne extends StatefulWidget {
#override
_BlockOneState createState() => _BlockOneState();
}
class _BlockOneState extends State<BlockOne> {
#override
Widget build(BuildContext context) {
return LayoutBuilder(
builder: (context, constraints) {
if (constraints.maxWidth > 1024)
return Desktop();
else
return Mobile();
},
);
}
}
class Desktop extends StatefulWidget {
#override
_DesktopState createState() => _DesktopState();
}
class _DesktopState extends State<Desktop> {
#override
Widget build(BuildContext context) {
return Container(
child: Text('Desktop'),
);
}
}
class Mobile extends StatefulWidget {
#override
_MobileState createState() => _MobileState();
}
class _MobileState extends State<Mobile> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: CustomPaint(
size: const Size(double.infinity, double.infinity),
painter: MobilePainter(),
),
);
}
}
class MobilePainter extends CustomPainter {
#override
void paint(Canvas canvas, Size size) {
final height = size.height;
final width = size.width;
Paint paint = Paint();
paint.color = Colors.black;
Path background = Path();
background.addRect(Rect.fromLTRB(0, 0, width, height));
canvas.drawPath(background, paint);
}
#override
bool shouldRepaint(covariant CustomPainter oldDelegate) {
return oldDelegate != this;
}
}

Check the following. Here I am using a list view to show contents. In flutter it is important to understand the concept that constraints go down from parent to child and the size goes up from a child to parent.
Here is demo of how this can be achieved. dartpad demo
If the child doesn't define a size then in ListView you have to define the itemExtent.
import 'package:flutter/material.dart';
// import 'blocks/block1.dart';
// import 'blocks/block2.dart';
// import 'blocks/block3.dart';
// import 'blocks/block4.dart';
void main() {
runApp(ScrollableView());
}
class ScrollableView extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: Home(),
);
}
}
class Home extends StatefulWidget {
#override
_HomeState createState() => _HomeState();
}
class _HomeState extends State<Home> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: ListView(
// child: Column(
shrinkWrap: true,
itemExtent: 200,
children: <Widget>[
BlockOne(),
Container(
color: Colors.black,
),
BlockOne(),
Text('Hello'),
BlockOne(),
Container(
color: Colors.black,
),
BlockOne(),
//BlockTwo(),
//BlockThree(),
//BlockFour()
],
// ),
));
}
}
class BlockOne extends StatefulWidget {
#override
_BlockOneState createState() => _BlockOneState();
}
class _BlockOneState extends State<BlockOne> {
#override
Widget build(BuildContext context) {
return LayoutBuilder(
builder: (context, constraints) {
// if (constraints.maxWidth > 1024)
// return Desktop();
// else
return Mobile();
},
);
}
}
class Desktop extends StatefulWidget {
#override
_DesktopState createState() => _DesktopState();
}
class _DesktopState extends State<Desktop> {
#override
Widget build(BuildContext context) {
return Container(
child: Text('Desktop'),
);
}
}
class Mobile extends StatefulWidget {
#override
_MobileState createState() => _MobileState();
}
class _MobileState extends State<Mobile> {
#override
Widget build(BuildContext context) {
return CustomPaint(
size: const Size(double.infinity, double.infinity),
painter: MobilePainter(),
);
}
}
class MobilePainter extends CustomPainter {
#override
void paint(Canvas canvas, Size size) {
final height = size.height;
final width = size.width;
Paint paint = Paint();
paint.color = Colors.green;
Path background = Path();
background.addRect(Rect.fromLTRB(0, 0, width, height));
canvas.drawPath(background, paint);
}
#override
bool shouldRepaint(covariant CustomPainter oldDelegate) {
return oldDelegate != this;
}
}

Related

How to scroll the listview or perform any other operation when popupmenu entry appears?

I have created listView which contains 10 container.I trigger the _showCustomMenu() method while longpress the container to show popupmenu.
But could not scroll the listview when popup menu appears.I want to scroll the listview even when popupmenu appears.
This is my code:
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter/rendering.dart';
void main() => runApp(MyApp());
///
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Popup Menu Usage',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Popup Menu Usage'),
);
}
}
///
class MyHomePage extends StatefulWidget {
///
MyHomePage({Key key, this.title}) : super(key: key);
///
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
var _tapPosition;
void _showCustomMenu() {
final RenderBox overlay = Overlay.of(context).context.findRenderObject();
showMenu(
context: context,
items: <PopupMenuEntry<int>>[PopUpMenuEntry()],
position: RelativeRect.fromRect(
_tapPosition &
const Size(40, 40), // smaller rect, the touch area
Offset.zero & overlay.size // Bigger rect, the entire screen
));
}
void _storePosition(TapDownDetails details) {
_tapPosition = details.globalPosition;
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: GestureDetector(
onTapDown:_storePosition,
onLongPress: (){
_showCustomMenu();
},
child:ListView.builder(
itemCount: 10,
itemBuilder: (context,index) {
return Container(height: 500,child: Center(child: Text(index.toString(),style: TextStyle(fontSize: 16),),),);
},
),
),
);
}
}
///
// ignore: must_be_immutable
class PopUpMenuEntry extends PopupMenuEntry<int> {
#override
double height = 100;
// height doesn't matter, as long as we are not giving
// initialValue to showMenu().
#override
bool represents(int n) => n == 1 || n == -1;
#override
PopUpMenuEntryState createState() => PopUpMenuEntryState();
}
///
class PopUpMenuEntryState extends State<PopUpMenuEntry> {
void _onPressed() {
Navigator.pop(context);
}
#override
Widget build(BuildContext context) {
return Column(
children: <Widget>[
FlatButton(onPressed: _onPressed, child: Text('Copy',style: TextStyle(fontSize: 20),)),
],
);
}
}
This is the Output:
Please help me to solve this or give me suggestion for any other popup widgets to overcome this problem.

flutter change background on click

I need to change the background color by clicking on the screen to random, I can't figure out how to do it.
import 'package:flutter/widgets.dart';
import 'dart:math';
main() => runApp(
Directionality(
textDirection: TextDirection.ltr,
child: Container(
color: Color((Random().nextDouble() * 0xFFFFFF).toInt()).withOpacity(1.0),
child: MyApp(),
),
),
);
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Container(
child: GestureDetector( // используется как обычный виджет
onTap: () { // одно из свойств GestureDetector
// Этот метод будет вызван, когда дочерний элемент будет нажат
print('You pressed me');
},
),
);
}
}
1. You need to make a StateFulWidget because you want to change the background when tapped.
2. You need to make a list of colors that can be set as the background color.
3. You need to add a variable which holds the current background color's index in the list of colors.
4. You need to change this variable when tapped to set a new background color.
Note: If you want to have random colors you can check the random_color package which is easy to use.
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: MyHomePage(title: "Title"),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
MyHomePageState createState() => MyHomePageState();
}
class MyHomePageState extends State<MyHomePage> {
List<Color> _colors = [Colors.blue, Colors.red, Colors.green, Colors.yellow];
var _index = 0;
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: _colors[_index % _colors.length],
body: GestureDetector(
onTap: () {
setState(() {
_index++;
});
},
child: Stack(
children: <Widget>[
Container(
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height,
color: Colors.transparent,
),
],
)
)
);
}
To do this you want to store the color as part of the state of the widget that you want. Then, when you detect a press, you can change the color and call setState to trigger a rebuild to show the new color. This involves modifying your code to use a StatefulWidget as I have done below.
The following code uses the exact same widgets as your original, with just modifications to make the necessary parts stateful.
import 'package:flutter/widgets.dart';
import 'dart:math';
main() => runApp(
Directionality(
textDirection: TextDirection.ltr,
child: MyApp(),
),
);
class MyApp extends StatefulWidget {//Changed to a `StatefulWidget`
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
//Store the color as a part of your `State`
Color color = Color((Random().nextDouble() * 0xFFFFFF).toInt()).withOpacity(1.0);
#override
Widget build(BuildContext context) {
return Container(
color: color,
child: Container(
child: GestureDetector(
onTap: () {
print('You pressed me');
//`setState` rebuilds your widget to show the new color
//It's not possible to use a `StatelessWidget` here
setState(() {
color = Color((Random().nextDouble() * 0xFFFFFF).toInt()).withOpacity(1.0);
});
},
),
),
);
}
}
Try this
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData.dark().copyWith(scaffoldBackgroundColor: darkBlue),
debugShowCheckedModeBanner: false,
home: Home(),
);
}
}
class Home extends StatefulWidget {
#override
_HomeState createState() => _HomeState();
}
class _HomeState extends State<Home> {
Color color;
#override
void initState() {
color = Color((Random().nextDouble() * 0xFFFFFF).toInt()).withOpacity(1.0);
super.initState();
}
#override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () {
setState(() {
color = Color((Random().nextDouble() * 0xFFFFFF).toInt())
.withOpacity(1.0);
});
},
child: Scaffold(
backgroundColor: color,
),
);
}
}

There are some parts that I don't understand well about Flutter's Key

I practiced after watching a video explaining Flutter's Key.
https://api.flutter.dev/flutter/foundation/Key-class.html
This video shows an example of changing the location of a container with a specific color. (About 1 minute and 50 seconds)
In the video, the statefulwidget says that without a key, the location will not change.
But I wrote the example code myself and confirmed that it worked without giving a key to the stateful widget.
I think I wrote the example code wrong. Below is the code I wrote.
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
home: KeyPractice(),
);
}
}
class StatefulColorfulTile extends StatefulWidget {
StatefulColorfulTile({#required this.color});
final Color color;
#override
_StatefulColorfulTileState createState() => _StatefulColorfulTileState();
}
class _StatefulColorfulTileState extends State<StatefulColorfulTile> {
#override
Widget build(BuildContext context) {
return Container(
width: 100,
height: 100,
color: widget.color,
);
}
}
class KeyPractice extends StatefulWidget {
#override
_KeyPracticeState createState() => _KeyPracticeState();
}
class _KeyPracticeState extends State<KeyPractice> {
List<Widget> tiles;
#override
void initState() {
super.initState();
tiles = [
StatefulColorfulTile(
color: Colors.blueAccent,
),
StatefulColorfulTile(
color: Colors.amber,
),
];
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Row(
children: tiles,
),
),
floatingActionButton: FloatingActionButton(
child: Icon(
Icons.autorenew,
),
onPressed: () {
setState(() {
tiles.insert(1, tiles.removeAt(0));
});
},
),
);
}
}
The above codes switch positions with each other.
What happens to the example of how the widget does not reposition each other when the stateful widget in the video does not assign keys?
And I understand that the key works only on the Stateful widget, does the Stateless use the key?
And I understood that Key only works with the Stateful widget. I wonder if the Stateless widget uses a key.
If I misunderstood, please teach me.
You're storing the color in the State of KeyPractice. The example they use stores it in the State of the child, in your case: StatefulColorfulTile.
Below is an example of the use of keys to correctly reposition widgets like you're trying to do. My example ended up very similar to what's shown on this medium article. Removing the keys here prevents the widgets from reflecting the color swap, but the use of the keys allows for the intended behavior.
import 'package:flutter/material.dart';
import 'dart:math';
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',
home: KeyPractice(),
);
}
}
class StatefulColorfulTile extends StatefulWidget {
StatefulColorfulTile({Key key}) : super(key: key);
#override
_StatefulColorfulTileState createState() => _StatefulColorfulTileState();
}
class _StatefulColorfulTileState extends State<StatefulColorfulTile> {
final Color myColor = UniqueColorGenerator.getColor();
#override
Widget build(BuildContext context) {
return Container(
width: 100,
height: 100,
color: myColor,
);
}
}
class KeyPractice extends StatefulWidget {
#override
_KeyPracticeState createState() => _KeyPracticeState();
}
class _KeyPracticeState extends State<KeyPractice> {
List<Widget> tiles;
#override
void initState() {
super.initState();
tiles = [
StatefulColorfulTile(key: UniqueKey()),
StatefulColorfulTile(key: UniqueKey()),
];
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Row(
children: tiles,
),
),
floatingActionButton: FloatingActionButton(
child: Icon(
Icons.autorenew,
),
onPressed: () {
setState(() {
tiles.insert(1, tiles.removeAt(0));
});
},
),
);
}
}
class UniqueColorGenerator {
static Random random = new Random();
static Color getColor() {
return Color.fromARGB(255, random.nextInt(255), random.nextInt(255), random.nextInt(255));
}
}

Call a setState of a statefull widget from the stateless widget

I have a stateless widget class that has a widget whose movements need to be tracked. I cannot keep this widget inside the stateful widgets as I don't want the state of this widget to be refreshed.
I have the following code.
import 'package:flutter/material.dart';
import 'package:control_pad/control_pad.dart';
void main() => runApp(new MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new MaterialApp(
home: new MyHomePage(),
);
}
}
class MyHomePage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Container(
color: Colors.white,
child: Column(
children: <Widget>[
Expanded(
child: JoystickView(
onDirectionChanged: (degree, direction) {
//Change the state here.
},
),
),
Expanded(
child: MyStateFull(),
),
],
),
);
}
}
class MyStateFull extends StatefulWidget {
#override
_MyStateFullState createState() => _MyStateFullState();
}
class _MyStateFullState extends State<MyStateFull> {
double degree = 10;
double direction = 10;
//Call this from the stateless Widget
void changedDirection(degree, direction) {
setState(() {
this.degree = degree;
this.direction = direction;
});
}
#override
Widget build(BuildContext context) {
return Container(
child: Text(
"The degree Moved is $degree and the direction is $direction",
style: TextStyle(fontSize: 25, color: Colors.black),
),
);
}
}
This code produces the following output.
I want the direction and degree values to be changed as the joystick is moved.
Thank You.
I tried it myself and found the solution. This can be done using streams. I will post the code just in case someone needs it in the future.
import 'package:flutter/material.dart';
import 'package:control_pad/control_pad.dart';
class MyStateLess extends StatelessWidget {
StreamController<List<double>> _controller = StreamController<List<double>>();
GlobalKey<_MyStateFullState> statefulKey = new GlobalKey<_MyStateFullState>();
#override
Widget build(BuildContext context) {
return Column(
children: <Widget>[
JoystickView(
onDirectionChanged: (degree, direction) {
List<double> temp = new List<double>();
temp.add(degree);
temp.add(direction);
_controller.add(temp);
},
),
MyStateFull(stream: _controller.stream, key: statefulKey),
],
);
}
}
class MyStateFull extends StatefulWidget {
final Stream<List<double>> stream;
MyStateFull({Key key, #required this.stream}) : super(key: key);
#override
_MyStateFullState createState() => _MyStateFullState();
}
class _MyStateFullState extends State<MyStateFull> {
double _degree = 0.0;
double _direction = 0.0;
#override
void initState() {
super.initState();
widget.stream.listen((event) {
setState(() {
_degree = event[0];
_direction = event[1];
});
});
}
#override
Widget build(BuildContext context) {
return Container(
child: Text("$_degree, $_direction"),
);
}
}

flutter display 3d objects flutter_3d_obj

I have managed to display 3d objects on flutter using /flutter_3d_obj. I'm now trying to get coordinates of a click point in flutter. How do i go about it.Or what is the best way to display 3d objects in flutter? and have the ability to get coordinates of a point.
You can wrap your 3Dmodel Widget with a GestureDectector Widget and use the onTapDownDetail property to listen to touch position
import 'package:flutter/material.dart';
import 'package:flutter_3d_obj/flutter_3d_obj.dart';
void main() {
runApp(new MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'Flutter Demo',
home: new MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
#override
MyHomePageState createState() => new MyHomePageState();
}
class MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('3D Touchable Model'),
),
body: Touch3DModel());
}
}
class Touch3DModel extends StatefulWidget {
#override
State<StatefulWidget> createState() {
return Touch3DModelState();
}
}
class Touch3DModelState extends State<Touch3DModel> {
double obj_width = 100;
double obj_height = 100;
double x_coord = 100.0;
double y_coord = 100.0;
void onTapDown(BuildContext context, TapDownDetails details) {
print('${details.globalPosition}');
final RenderBox box = context.findRenderObject();
final Offset localOffset = box.globalToLocal(details.globalPosition);
setState(() {
x_coord = localOffset.dx;
y_coord = localOffset.dy;
});
}
#override
Widget build(BuildContext context) {
return GestureDetector(
onTapDown: (TapDownDetails details) => onTapDown(context, details),
child: Stack(fit: StackFit.expand, children: <Widget>[
Container(color: Colors.grey),
Positioned(
child: Object3D(
size: const Size(obj_width, obj_height),
path: "assets/file.obj",
asset: true,
),
left: x_coord - obj_width / 2, // To offset the object possitioning from top left of the object to the center.
top: y_coord - obj_height / 2,
)
]),
);
}
}
Note: Using flutter_3d_obj will require that you have a 3d obj that you built with something like blender or a similar tool.
Credit to aptik