Related
Anyone knows how to change icon and text color depending on the background color of the image or video?
The palette_generator package can help you find the most dominant color(s) in the image. You can use these color(s) to set the Text and Icon color.
Please check out the example code provided by the package author https://pub.dev/packages/palette_generator/example . The PaletteGenerator.fromImageProvider method can be used to get the color pallet from the image. You can use the following code from the example :
Future<void> _updatePaletteGenerator(Rect newRegion) async {
paletteGenerator = await PaletteGenerator.fromImageProvider(
widget.image,
size: widget.imageSize,
region: newRegion,
maximumColorCount: 20,
);
setState(() {});
}
....
Color dominantColor = paletteGenerator.dominantColor?.color;
....
Please see the entire working code below : (Add palette_generator: ^0.2.3 to your pubspec.yaml first)
import 'package:flutter/material.dart';
import 'package:palette_generator/palette_generator.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Palette Generator',
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: HomePage(),
);
}
}
class HomePage extends StatefulWidget {
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
Future _updateColors;
final List<PaletteColor> _colors = [];
int _currentIndex;
final List<String> _images = [
'https://picsum.photos/id/491/200/300',
'https://picsum.photos/id/400/200/300',
'https://picsum.photos/id/281/200/300'
];
#override
void initState() {
super.initState();
_currentIndex = 0;
_updateColors = _updatePalettes();
}
Future<bool> _updatePalettes() async {
for (final String image in _images) {
final PaletteGenerator generator =
await PaletteGenerator.fromImageProvider(NetworkImage(image));
_colors.add(generator.dominantColor != null
? generator.dominantColor
: PaletteColor(Colors.blue, 2));
}
setState(() {});
return true;
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Color Palette Generator Demo'),
elevation: 0,
backgroundColor: _colors.isNotEmpty
? _colors[_currentIndex].color
: Theme.of(context).primaryColor,
),
body: FutureBuilder<bool>(
future: _updateColors,
builder: (context, snapshot) {
if (snapshot.data == true)
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Container(
width: double.infinity,
height: 200,
color: _colors.isNotEmpty
? _colors[_currentIndex].color
: Colors.white,
child: PageView(
onPageChanged: (value) =>
setState(() => _currentIndex = value),
children: _images
.map((image) => Container(
padding: const EdgeInsets.all(16.0),
margin: const EdgeInsets.all(16.0),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(30.0),
image: DecorationImage(
image: NetworkImage(image),
fit: BoxFit.cover,
),
),
))
.toList(),
),
),
Expanded(
child: Container(
padding: const EdgeInsets.all(32.0),
width: double.infinity,
decoration: BoxDecoration(
color: _colors.isNotEmpty
? _colors[_currentIndex].color
: Colors.white),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Text(
"Color Palette",
style: TextStyle(
color: _colors.isNotEmpty
? _colors[_currentIndex].titleTextColor
: Colors.black,
fontWeight: FontWeight.bold,
fontSize: 30.0,
),
),
const SizedBox(height: 10.0),
Icon(
Icons.ac_unit,
size: 100,
color: _colors.isNotEmpty
? _colors[_currentIndex].bodyTextColor
: Colors.black,
)
],
),
),
),
],
);
return const Center(child: CircularProgressIndicator());
},
),
);
}
}
I'm trying to check if the keyboard is visible after tapping on the TextFormField by calling:
if (MediaQuery.of(context).viewInsets.bottom != 0) {
...
}
but as soon as I have this MediaQuery call in my code, the Keyboard doesn't even open anymore after tapping on the TextFormField...
Edited:
This is what happens when tapping on the TextFormField:
I added the code of the page which causes this faulty behavior:
class LearnPage extends StatefulWidget {
final int topicId;
final String topicName;
LearnPage(this.topicId, this.topicName);
#override
_LearnPageState createState() => _LearnPageState();
}
class _LearnPageState extends State<LearnPage> {
final mainCaardIndex = ValueNotifier<int>(0);
PageController _mainCaardController;
PageController _inputCaardController;
List<CaardM> caards;
List<PageM> mainCaardList = [];
List<List<PageM>> inputCaardList = [];
List<List<TextEditingController>> textControllers = [];
Future<void> async_init() async {
List<CaardM> caardList =
await DatabaseProviderCaard.db.getCaards(widget.topicId);
caards = caardList;
setState(() {});
}
bool _keyboardIsVisible() {
return !(MediaQuery.of(context).viewInsets.bottom == 0.0);
}
#override
void initState() {
async_init();
_mainCaardController = PageController();
_inputCaardController = PageController();
super.initState();
}
#override
void dispose() {
_mainCaardController.dispose();
_inputCaardController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.lightBlue,
title: Center(
child: Text(
widget.topicName,
textAlign: TextAlign.center,
style: TextStyle(fontWeight: FontWeight.bold),
),
),
actions: [
!_keyboardIsVisible()
? IconButton(
icon: Icon(Icons.check_circle_outline),
tooltip: 'Validate',
onPressed: validate,
)
: IconButton(
icon: Icon(Icons.keyboard_hide),
onPressed: () {
FocusManager.instance.primaryFocus.unfocus();
},
),
],
),
body: Column(
children: [
Expanded(
flex: 3,
child: FutureBuilder(
future: getMainContent(),
builder: (context, AsyncSnapshot<int> snapshotMain) {
if (snapshotMain.connectionState == ConnectionState.done) {
return PageView.builder(
itemCount: snapshotMain.data,
controller: _mainCaardController,
onPageChanged: (position) {
mainCaardIndex.value = position;
mainCaardIndex.notifyListeners();
_inputCaardController.jumpToPage(0);
},
itemBuilder: (context, position) {
return LearnMainCaard(
mainCaardList[position].title,
mainCaardList[position].content,
);
},
);
} else {
return CircularProgressIndicator();
}
},
),
),
Expanded(
flex: 5,
child: FutureBuilder(
future: getInputContent(),
builder: (context, AsyncSnapshot<int> snapshotInput) {
if (snapshotInput.connectionState == ConnectionState.done) {
return ValueListenableBuilder(
valueListenable: mainCaardIndex,
builder: (context, value, _) {
return PageView.builder(
itemCount: snapshotInput.data,
controller: _inputCaardController,
itemBuilder: (context, position) {
return LearnInputCaard(
inputCaardList[mainCaardIndex.value][position].title,
textControllers[mainCaardIndex.value][position],
);
},
);
},
);
} else {
return CircularProgressIndicator();
}
},
),
),
],
),
);
}
Future<int> getMainContent() async {
List<PageM> caardPages;
mainCaardList.clear();
for (var i = 0; i < caards.length; i++) {
caardPages = await DatabaseProviderPage.db.getPages(caards[i].id);
if (caards[i].pageAmount > 1) {
mainCaardList.add(caardPages[0]);
}
}
return mainCaardList.length;
}
Future<int> getInputContent() async {
List<PageM> caardPages = [];
List<PageM> list = [];
inputCaardList.clear();
for (var i = 0; i < caards.length; i++) {
caardPages = await DatabaseProviderPage.db.getPages(caards[i].id);
if (caards[i].pageAmount > 1) {
addController(caards[i].pageAmount - 1);
list = [];
for (var i = 1; i < caardPages.length; i++) {
list.add(caardPages[i]);
}
inputCaardList.add(list);
}
}
return inputCaardList[mainCaardIndex.value].length;
}
void addController(int controllerAmount) {
List<TextEditingController> currentTextControllers = [];
print('addController called');
currentTextControllers.clear();
currentTextControllers = List.generate(
controllerAmount, (index) => TextEditingController()
);
textControllers.add(currentTextControllers);
}
And here the LearnInputCaard widget:
import 'package:flutter/material.dart';
class LearnInputCaard extends StatefulWidget {
final String title;
final TextEditingController textController;
LearnInputCaard(
this.title,
this.textController,
);
#override
_LearnInputCaardState createState() => _LearnInputCaardState();
}
class _LearnInputCaardState extends State<LearnInputCaard> {
#override
Widget build(BuildContext context) {
return Container(
child: Card(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20),
),
margin: EdgeInsets.all(20),
color: Colors.amberAccent.shade100,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
children: [
Expanded(
flex: 1,
child: Text(
widget.title,
style: TextStyle(fontSize: 20),
),
),
Divider(color: Colors.black38,),
Expanded(
flex: 10,
child: Container(
padding: EdgeInsets.all(10.0),
child: TextFormField(
controller: widget.textController,
maxLines: 30,
decoration: InputDecoration(
hintText: "Enter content",
border: InputBorder.none,
),
),
),
)
],
),
),
),
);
}
}
you need to check MediaQuery.of(context).viewInsets.bottom == 0.0
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',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Keyboard Visibility Demo'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Container(
width: double.infinity,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
_keyboardIsVisible()
? Text(
"Keyboard is visible",
style: Theme.of(context)
.textTheme
.display1
.copyWith(color: Colors.blue),
)
: RichText(
text: TextSpan(children: [
TextSpan(
text: "Keyboard is ",
style: Theme.of(context)
.textTheme
.display1
.copyWith(color: Colors.blue),
),
TextSpan(
text: "not ",
style: Theme.of(context)
.textTheme
.display1
.copyWith(color: Colors.red),
),
TextSpan(
text: "visible",
style: Theme.of(context)
.textTheme
.display1
.copyWith(color: Colors.blue),
)
]),
),
SizedBox(
height: 20,
),
Container(
width: 200.0,
child: TextField(
style: Theme.of(context).textTheme.display1,
decoration: InputDecoration(
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(
color: Colors.blue,
),
borderRadius: BorderRadius.circular(10.0),
),
),
),
)
],
),
));
}
bool _keyboardIsVisible() {
return !(MediaQuery.of(context).viewInsets.bottom == 0.0);
}
}
The problem is that you get the context from the parent widget.
If you call:
MediaQuery.of(context);
in the same widget where your forms are, you shouldn't get this behavior.
You need to define a GlobalKey<FormState> in your highest widget and pass this one down. Then it works. I defined it first in my SafeArea and therefore it failed and I had the same problem with the keyboard.
Here are some snippets of my code. I have a PageController and use two different forms on my two pages.
class OnboardingScaffold extends HookConsumerWidget {
OnboardingScaffold({Key? key}) : super(key: key);
// here you define your GlobalKeys
final _formKeyLogin = GlobalKey<FormState>();
final _formKeyApply = GlobalKey<FormState>();
#override
Widget build(BuildContext context, WidgetRef ref) {
final controller = usePageController();
bool isKeyboard = MediaQuery.of(context).viewInsets.bottom != 0;
return Scaffold(
body: Container(
padding: !isKeyboard
? const EdgeInsets.only(bottom: 80)
: const EdgeInsets.only(bottom: 0),
child: PageView(
controller: controller,
children: [
// here you pass these keys into your child Widget
LoginSafeArea(
formKey: _formKeyLogin,
),
ApplySafeArea(
formKey: _formKeyApply,
),
],
),
),
bottomSheet: !isKeyboard
? Container(height: 80)
: Container(height: 0),
);
}
}
The child Widget should contain a Form Widget:
class LoginSafeArea extends HookConsumerWidget {
const LoginSafeArea({Key? key, required this.formKey}) : super(key: key);
final GlobalKey<FormState> formKey;
#override
Widget build(BuildContext context, WidgetRef ref) {
return SafeArea(
child: Center(
child: Form(
key: formKey,
child: SingleChildScrollView(
physics: const BouncingScrollPhysics(),
padding: const EdgeInsets.only(left: 24.0, right: 24.0),
child: Column(
children: <Widget>[
const EmailFieldWidget(),
const SizedBox(height: 8.0),
const PasswordFieldWidget(),
const SizedBox(height: 16.0),
LoginButtonWidget(
formKey: formKey,
),
const SizedBox(height: 8.0),
],
),
),
),
),
);
}
}
I have to implement a horizontal scroll list in flutter.I could do that and have included the code below(The code is still to be modified but the base of the code is good enough to put in the pictures and other such details)
But the problem is the minus bar below the horizontal scroll.I don't know what feature in flutter allows to do that.I search many things but other than radio boxes,check boxes, switches,etc I am not able to find any details of it.Please have a look at the screenshot of the app ,I have indicated the minus bar control in red.Home screen,the minus bar indicated in red
The code I have written:
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.black ,
body: Container(
margin: EdgeInsets.symmetric(vertical: 20.0),
height: 500,
child: ListView(
// This next line does the trick.
scrollDirection: Axis.horizontal,
shrinkWrap: true,
children: <Widget>[
Container(
width:400 ,
color: Colors.red,
),
Container(
width: 400.0,
color: Colors.blue,
),
Container(
width: 400.0,
color: Colors.green,
),
],
),
)
);
}
}
What you want to look for is not ListView but PageView here is a small code sample to try in DartPad and see how you could make your layout.
Basically I am using a PageController to change the current page by taping on certain widgets.
Code
import 'package:flutter/material.dart';
final Color darkBlue = Color.fromARGB(255, 18, 32, 47);
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData.dark().copyWith(scaffoldBackgroundColor: darkBlue),
debugShowCheckedModeBanner: false,
home: Scaffold(body: MyWidget()),
);
}
}
class MyWidget extends StatefulWidget {
#override
_MyWidgetState createState() => _MyWidgetState();
}
class _MyWidgetState extends State<MyWidget> {
final _items = [Colors.red, Colors.blue, Colors.yellow];
final _pageController = PageController();
int _currentPageNotifier = 0;
final double _indicatorWidth = 30;
Widget _buildPageView() {
return PageView.builder(
controller: _pageController,
itemCount: _items.length,
itemBuilder: (context, index) => Center(
child: FlutterLogo(
colors: _items[index],
size: 50,
),
),
onPageChanged: (int index) =>
setState(() => _currentPageNotifier = index),
);
}
Widget _buildIndicator() {
List<Widget> itemWidgets = [];
for (int index = 0; index < _items.length; index++) {
itemWidgets.add(GestureDetector(
onTap: () => _pageController.animateToPage(
index,
duration: Duration(milliseconds: 300),
curve: Curves.ease,
),
child: Container(
decoration: BoxDecoration(
color: _currentPageNotifier == index
? Colors.green
: Colors.grey,
borderRadius: BorderRadius.circular(9),
),
margin: EdgeInsets.only(right: 10),
width: _indicatorWidth,
height: 8,
),
));
}
return Positioned(
bottom: MediaQuery.of(context).size.height / 2 - 50,
left: MediaQuery.of(context).size.width / 2 -
_items.length * _indicatorWidth +
_items.length * 10,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: itemWidgets,
),
);
}
#override
Widget build(BuildContext context) {
return Stack(
children: <Widget>[
_buildPageView(),
_buildIndicator(),
],
);
}
}
I have a navigation bar at the bottom of a multi-page form, with buttons to go back or forward, and an indicator for the current page.
Right now, I have the indicator place in a Row above another Row that contains the buttons, like this:
This works, and it will work even on small display sizes. However, I would rather have the indicators placed on the same row as the buttons if there is enough space, like this (except that the indicator is not centered):
The problem is, this could be too wide for some devices, especially if there are more than just a few pages. In that case, I would like either the page indicator or the buttons to "wrap" to a new row, as in the current design.
It would be easy to put everything in a Wrap, but that will make the NEXT-button wrap instead of the page indicator, since that is the last element.
Is there an easy way to make the middle element wrap onto a new row if needed? Or does one have to resort to the black magic of manually calculating sizes and creating two different layouts?
The easiest solution is to add invisible wrap to calculate the height.
import 'package:flutter/material.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: Scaffold(
body: SafeArea(
child: MyHomePage(),
),
),
);
}
}
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int currentPageIndex = 1;
int pageCount = 8;
#override
Widget build(BuildContext context) {
return Column(
children: [
Expanded(
child: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Text(
currentPageIndex.toString(),
),
Row(
mainAxisSize: MainAxisSize.min,
children: [
RaisedButton(
child: Text('+1 page'),
onPressed: () => setState(() => ++pageCount),
),
SizedBox(
width: 10,
),
RaisedButton(
child: Text('-1 page'),
onPressed: () => setState(() => --pageCount),
),
],
)
],
),
),
),
Container(
child: _BottomNavigation(
onPrev: () => setState(() => --currentPageIndex),
onNext: () => setState(() => ++currentPageIndex),
currentCount: currentPageIndex,
totalCount: pageCount,
),
),
],
);
}
}
class _BottomNavigation extends StatelessWidget {
const _BottomNavigation({
Key key,
#required this.totalCount,
#required this.currentCount,
#required this.onNext,
#required this.onPrev,
}) : assert(totalCount != null),
assert(currentCount != null),
assert(onNext != null),
assert(onPrev != null),
super(key: key);
final void Function() onPrev;
final void Function() onNext;
final int totalCount;
final int currentCount;
#override
Widget build(BuildContext context) {
return Stack(
children: [
buildHelper(),
buildIndicatorBar(),
Positioned(
bottom: 0,
left: 0,
right: 0,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
_button(
'<< PREIOUS',
onPrev,
isVisible: currentCount > 1,
),
_button(
'NEXT >>',
onNext,
isVisible: currentCount < totalCount,
),
],
),
),
],
);
}
Wrap buildHelper() {
return Wrap(
children: List.generate(
totalCount,
(index) {
return Container(
width: index == 0 ? 250 : 15,
height: 20,
);
},
),
);
}
Row buildIndicatorBar() {
return Row(
mainAxisAlignment: MainAxisAlignment.center,
children: List.generate(totalCount, (index) {
var isLast = totalCount != index + 1;
var isCurrent = currentCount == index + 1;
return Container(
height: isCurrent ? 20 : 10,
width: isCurrent ? 20 : 10,
margin: EdgeInsets.only(right: isLast ? 10 : 0),
decoration: BoxDecoration(
shape: BoxShape.circle,
color: isCurrent ? Colors.blueAccent : null,
border: Border.all(
color: Colors.blueAccent,
),
),
);
}),
);
}
Widget _button(String text, void Function() onPress, {bool isVisible}) {
return Visibility(
visible: isVisible,
child: GestureDetector(
onTap: onPress,
child: Text(text),
),
);
}
}
I used a couple other threads to create an app where you type in some text in a textfield and when you press the button a default container is added to a list with the text in one of the fields. However when I type the text and add the widget the text is changed for all entries instead of just for the one that was added. This is my code:
import 'dart:core';
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'Flutter Demo',
theme: new ThemeData(
primarySwatch: Colors.blue,
),
home: new MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => new _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int count = 0;
TextEditingController noteSend = TextEditingController();
#override
Widget build(BuildContext context) {
List<Widget> children = new List.generate(
count,
(int i) => new InputWidget(
i,
noteRec: noteSend.text,
));
return new Scaffold(
appBar: new AppBar(title: new Text('some title')),
body: Column(
children: <Widget>[
Container(
child: TextField(
controller: noteSend,
),
color: Colors.lightBlueAccent,
width: 150,
height: 50,
),
Expanded(
child: ListView(
children: children,
scrollDirection: Axis.vertical,
),
),
],
),
floatingActionButton: new FloatingActionButton(
child: new Icon(Icons.add),
onPressed: () {
setState(() {
count = count + 1;
});
},
));
}
}
class InputWidget extends StatefulWidget {
final int index;
final String noteRec;
InputWidget(this.index, {Key key, this.noteRec}) : super(key: key);
#override
_InputWidgetState createState() => _InputWidgetState();
}
class _InputWidgetState extends State<InputWidget> {
#override
Widget build(BuildContext context) {
return new Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(10),
),
child: Row(
children: <Widget>[
Column(
children: <Widget>[
Icon(
Icons.image,
size: 75,
)
],
),
Container(
margin: EdgeInsets.only(left: 80, right: 30),
child: Column(
children: <Widget>[
Text('Note'),
],
),
),
Column(
children: <Widget>[
Text("${widget.noteRec}"),
],
),
],
),
);
}
}
How can I make the Text different with every entry?
List<Widget> children = new List.generate(
count,
(int i) => new InputWidget(
i,
noteRec: noteSend.text,
));
In this code, you set the input text for all the elements in children. It's the reason all the entries are changed to the same text. You can save the text to a list of the string when you press the save button and call it in List.generate:
List<Widget> children = new List.generate(
count,
(int i) => new InputWidget(
i,
noteRec: listString[i],
));
Try this code
Container(
height: 200,
child: Column(
children: <Widget>[
Expanded(
child: buildGridView(),
)
],
),
),
Then call the buildGridView that will return Widgets
Widget buildGridView() {
return Text('text here'); // your future widget
}