I want to do this:
This is the code from the video above:
import 'dart:async';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:pomodoro/5.hourglass_animation/countdown_timer/responsive_web.dart';
class StartPomodoro extends StatefulWidget {
const StartPomodoro({
super.key,
});
//final DateTime end;
#override
State<StartPomodoro> createState() => _StartPomodoroState();
}
class _StartPomodoroState extends State<StartPomodoro>
with TickerProviderStateMixin {
final now = DateTime.now();
List<bool> isSelected = [true, false];
late Timer timer;
late AnimationController controller;
String get countText {
Duration count = controller.duration! * controller.value;
return controller.isDismissed
? '${controller.duration!.inHours.toString().padLeft(2, '0')}:${(controller.duration!.inMinutes % 60).toString().padLeft(2, '0')}:${(controller.duration!.inSeconds % 60).toString().padLeft(2, '0')}'
: '${count.inHours.toString().padLeft(2, '0')}:${(count.inMinutes % 60).toString().padLeft(2, '0')}:${(count.inSeconds % 60).toString().padLeft(2, '0')}';
}
double progress = 1.0;
bool LongBreak = true;
void notify() {
if (countText == '00:00:00') {}
}
#override
void initState() {
super.initState();
controller = AnimationController(
vsync: this,
duration: const Duration(seconds: 0),
);
controller.addListener(() {
notify();
if (controller.isAnimating) {
setState(() {
progress = controller.value;
});
} else {
setState(() {
progress = 1.0;
LongBreak = true;
});
}
});
}
#override
void dispose() {
controller.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
backgroundColor:
LongBreak ? const Color(0xffD94530) : const Color(0xff6351c5),
body: GestureDetector(
onTap: () {
if (controller.isDismissed) {
showModalBottomSheet(
context: context,
builder: (context) => SizedBox(
height: 300,
child: CupertinoTimerPicker(
initialTimerDuration: controller.duration!,
onTimerDurationChanged: (time) {
setState(() {
controller.duration = time;
});
},
),
),
);
}
},
child: AnimatedBuilder(
animation: controller,
builder: (context, child) {
return Stack(
children: <Widget>[
Align(
alignment: Alignment.bottomCenter,
child: Container(
color: const Color(0xffD94530),
height: controller.value * 580,
),
),
Column(
mainAxisAlignment: MainAxisAlignment.start,
mainAxisSize: MainAxisSize.max,
verticalDirection: VerticalDirection.up,
children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: ResponsiveWeb(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceAround,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Padding(
padding: const EdgeInsets.symmetric(
vertical: 2.0, horizontal: 15.0),
child: Container(
width: MediaQuery.of(context).size.width,
height: 210,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(20),
color: const Color(0xffFAFAFA)),
child: Container(
padding: const EdgeInsets.all(20.0),
child: Column(
mainAxisAlignment:
MainAxisAlignment.center,
crossAxisAlignment:
CrossAxisAlignment.start,
children: [
Expanded(
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Column(
children: [
Text(
"Hyper-focused on... (+add task)",
style: GoogleFonts.nunito(
fontSize: 20.0,
fontWeight:
FontWeight.w500,
),
),
],
),
),
),
const SizedBox(height: 10),
Center(
child: SingleChildScrollView(
child: Column(
mainAxisAlignment:
MainAxisAlignment.center,
children: [
SingleChildScrollView(
scrollDirection:
Axis.horizontal,
child: Row(
mainAxisAlignment:
MainAxisAlignment
.center,
children: [
Padding(
padding:
const EdgeInsets
.fromLTRB(
2, 0, 2, 0),
child: Text(
countText,
style: GoogleFonts
.nunito(
fontWeight:
FontWeight
.w500,
letterSpacing: 8,
fontSize: 57.0,
color: const Color(
0xff3B3B3B),
),
),
),
],
),
),
SingleChildScrollView(
scrollDirection:
Axis.horizontal,
child: Row(
mainAxisAlignment:
MainAxisAlignment
.start,
textDirection:
TextDirection.ltr,
mainAxisSize:
MainAxisSize.max,
crossAxisAlignment:
CrossAxisAlignment
.start,
children: [
Padding(
padding:
const EdgeInsets
.fromLTRB(
13, 0, 26, 0),
child: Text(
'Hours',
style: GoogleFonts
.nunito(
fontWeight:
FontWeight
.w500,
// letterSpacing:
// 2,
fontSize: 20.0,
color: const Color(
0xff3B3B3B),
),
),
),
Padding(
padding:
const EdgeInsets
.fromLTRB(
13, 0, 26, 0),
child: Text(
'Minutes',
style: GoogleFonts
.nunito(
fontWeight:
FontWeight
.w500,
// letterSpacing:
// 2,
fontSize: 20.0,
color: const Color(
0xff3B3B3B),
),
),
),
Padding(
padding:
const EdgeInsets
.fromLTRB(
0, 0, 0, 0),
child: Text(
'Seconds',
style: GoogleFonts
.nunito(
fontWeight:
FontWeight
.w500,
// letterSpacing:
// 2,
fontSize: 20.0,
color: const Color(
0xff3B3B3B),
),
),
),
],
),
),
],
),
),
),
],
),
),
),
),
const SizedBox(
height: 65,
),
Column(
// mainAxisAlignment: MainAxisAlignment.end,
crossAxisAlignment:
CrossAxisAlignment.stretch,
children: [
AnimatedBuilder(
animation: controller,
builder: (context, child) {
return Padding(
padding: const EdgeInsets.symmetric(
vertical: 10.0,
horizontal: 15.0),
child:
FloatingActionButton.extended(
elevation: 0,
backgroundColor:
const Color(0xffFAFAFA),
onPressed: () {
if (controller
.isAnimating) {
controller.stop();
setState(() {
LongBreak = false;
});
} else {
controller.reverse(
from: controller
.value ==
0
? 1.0
: controller
.value);
setState(() {
LongBreak = false;
});
}
},
icon: Icon(
controller.isAnimating
? Icons.pause_outlined
: Icons
.play_arrow_outlined,
size: 24,
color: const Color(
0xff3B3B3B),
),
label: Text(
controller.isAnimating
? "Pause"
: "Start",
style: GoogleFonts.nunito(
fontWeight:
FontWeight.w500,
letterSpacing: 2,
fontSize: 24.0,
color: const Color(
0xff3B3B3B),
),
)),
);
}),
],
),
],
),
),
),
],
),
],
);
}),
),
),
);
}
AnimationController _buildClockAnimation(TickerProvider tickerProvider) {
return AnimationController(
vsync: tickerProvider,
duration: const Duration(milliseconds: 750),
);
}
void _animateLeftDigit(
int prev,
int current,
AnimationController controller,
) {
final prevFirstDigit = (prev / 10).floor();
final currentFirstDigit = (current / 10).floor();
if (prevFirstDigit != currentFirstDigit) {
controller.forward();
}
}
}
And I decided to optimize the timer by adding a setting function. But it is difficult for me to add the animation, but the timer works fine. The only thing that doesn't work is the animation when I click “start”
This is what I mean:
Please click on the youtube video
https://www.youtube.com/watch?v=cuAS_Pk5cNk&ab_channel=lomipac
This is the code from the button of the second video above, where I want to initialize the animation:
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:google_fonts/google_fonts.dart';
import '../../controllers/countdown_controller.dart';
class StartStopGroupButton extends StatefulWidget {
const StartStopGroupButton({Key? key}) : super(key: key);
#override
State<StartStopGroupButton> createState() => _StartStopGroupButtonState();
}
class _StartStopGroupButtonState extends State<StartStopGroupButton>
with TickerProviderStateMixin {
String startPausedText = 'Start'.tr;
String stopText = 'stop'.tr;
final CountDownController _countDownController = Get.find();
#override
Widget build(BuildContext context) {
return Obx(() => Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Padding(
padding: const EdgeInsets.symmetric(
vertical: 10.0, horizontal: 15.0),
child: FloatingActionButton.extended(
heroTag: 'btn1',
elevation: 0,
backgroundColor: const Color(0xffFAFAFA),
onPressed: _countDownController.startPaused,
label: Text(
_countDownController.startPausedText.value,
style: GoogleFonts.nunito(
fontWeight: FontWeight.w500,
fontSize: 24.0,
color: const Color(0xff3B3B3B),
),
),
),
),
],
),
));
}
}
How can I link the animation to the start button from the second code?
Thanks for any help you can provide
Related
I am using react-native-tab-view, and trying to change the indicator width. I would like indicator width to be the same with the tab text. But What I did is just the default. I have tried in many ways, but always it gave me the wrong result. The tab bar should be scrollable horizontally as well. Could you check which part I should change?
This is the expected result :
ShowAllIndex Code :
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: colors.dark,
},
});
const ShowAllIndex = () => {
const { seller } = useSelector((s) => s.auth, shallowEqual);
const [routes] = useState([
{ key: 'best', title: 'BEST' },
{ key: 'jacket', title: '아우터' },
{ key: 'pants', title: '바지' },
{ key: 'skirts', title: '스커트' },
{ key: 'topClothe', title: '원피스' },
{ key: 'one', title: '바지' },
{ key: 'two', title: '스커트' },
{ key: 'three', title: '상의' },
]);
const renderScene = SceneMap({
best: ShowAllMainRoutes,
jacket: JacketRoutes,
pants: PantsRoutes,
skirts: SkirtsRoutes,
topClothe: TopClotheRoutes,
one: ShowAllMainRoutes,
two: JacketRoutes,
three: PantsRoutes,
});
return (
<ScrollView style={[styles.container, { marginTop: Global() }]}>
<CustomTabView
routes={routes}
renderScene={renderScene}
scrollEnabled={true}
tabStyle={{ width: 'auto' }}
showAll={true}
/>
</ScrollView>
);
};
export default ShowAllIndex;
CustomTabView code :
const initialLayout = { width: Dimensions.get('window').width };
const CustomTabView = ({
routes,
renderScene,
numberOfTabs,
indicatorWidth,
scrollEnabled = false,
tabStyle,
showAll,
indicatorStyle,
}) => {
const [index, setIndex] = useState(0);
const renderTabBar = (props) => (
<TabBar
{...props}
scrollEnabled={scrollEnabled}
indicatorStyle={[
indicatorStyle,
{
backgroundColor: colors.barbie_pink,
height: 2.5,
bottom: -1,
},
]}
style={[styles.tabBar]}
renderLabel={({ route, focused }) => {
return (
<Text
style={[styles.label, focused ? styles.activeLabel : styles.label]}
>
{route.title}
</Text>
);
}}
tabStyle={tabStyle}
/>
);
return (
<TabView
navigationState={{ index, routes }}
renderScene={renderScene}
renderTabBar={renderTabBar}
onIndexChange={setIndex}
initialLayout={initialLayout}
style={[styles.container]}
/>
);
};
const styles = StyleSheet.create({
container: {
backgroundColor: colors.dark,
},
scene: {
flex: 1,
marginTop: 5,
},
tabBar: {
backgroundColor: 'transparent',
shadowOpacity: 0,
elevation: 0,
borderBottomWidth: 0.5,
borderColor: colors.very_light_pink_two,
marginBottom: 5,
},
label: {
color: colors.very_light_pink_four,
fontSize: 14,
lineHeight: 20.8,
fontFamily: 'NotoSansCJKkr-Regular',
letterSpacing: -0.35,
},
activeLabel: {
color: colors.barbie_pink,
},
});
Thank you for answers!
Check this solution:
const TAB_MARGIN = 24;
<TabBar
...
scrollEnabled
renderIndicator={indicatorProps => {
const width = indicatorProps.getTabWidth(this.state.index) - TAB_MARGIN
return <TabBarIndicator {...indicatorProps} width={width} />
}}
indicatorStyle={{
backgroundColor: '#333',
height: 4,
left: TAB_MARGIN / 2,
}}
...
/>
I think the prop indicatorStyle in TabBar that can resolves you problem.
You can do the following:
<TabBar
scrollEnabled //add this line to make it scrollable
tabStyle={{width: 100}} //and this one to change the tab width
/>
I am not getting div in my dom. there is div with id of teams when I inspect app in browser and function returning both console.logs but no divs inside #teams div. :S
const Teams = () => {
const homeTeam = [
{ position: 0, team: "home", visible: true, x: "30%", y: "30%" },
{ position: 1, team: "home", visible: true, x: "40%", y: "30%" },
];
const awayTeam = [
{ position: 0, team: "away", visible: true, x: "70%", y: "70%" },
{ position: 1, team: "away", visible: true, x: "60%", y: "70%" },
];
const setTeams = (home, away) => {
const setPlayers = (team) => {
team.map((player, index) => {
console.log(player);
return (
<div className={`player ${player.team === "home" ? "home" : "away"}`} key={index}>
{player.position === 0 ? "G" : null}
</div>
);
});
};
setPlayers(home);
setPlayers(away);
};
return <div id="teams">{setTeams(homeTeam, awayTeam)}</div>;
};
export default Teams;
setPlayers and setTeams function does not return anything. Hence you do not see those element. Do this instead.
const Teams = () => {
const homeTeam = [
{ position: 0, team: "home", visible: true, x: "30%", y: "30%" },
{ position: 1, team: "home", visible: true, x: "40%", y: "30%" },
];
const awayTeam = [
{ position: 0, team: "away", visible: true, x: "70%", y: "70%" },
{ position: 1, team: "away", visible: true, x: "60%", y: "70%" },
];
const setTeams = (home, away) => {
const team = [...home,...away];
return team.map((player, index)=>
<div className={`player ${player.team === "home" ? "home" : "away"}`} key={index}>
{player.position === 0 ? "G" : null}
</div>
);
};
return <div id="teams">{setTeams(homeTeam, awayTeam)}</div>;
};
export default Teams;
Main Issue is when you are using your two functions :
setPlayers(home);
setPlayers(away);
within your setPlayers function. The returned map template (JSX) is actually returned to the respective calling function which is setPlayers() function (calling two times with different arguments). And here the execution of your program basically ends. No JSX is returned to main component's return() method.
To make sure the the JSX is returned with proper mapped JSX, JSX needs to return to the setTeams(homeTeam, awayTeam) so that it is rendered in our app. I have made sure I use one function setTeams twice in the code with two arrays homeTeam and awayTeam
You shoudl try this code instead :)
const App = () => {
const homeTeam = [
{ position: 0, team: "home", visible: true, x: "30%", y: "30%" },
{ position: 1, team: "home", visible: true, x: "40%", y: "30%" }
];
const awayTeam = [
{ position: 0, team: "away", visible: true, x: "70%", y: "70%" },
{ position: 1, team: "away", visible: true, x: "60%", y: "70%" }
];
const setTeams = (team) => {
return team.map((player, index) => (
<div
className={`player ${player.team === "home" ? "home" : "away"}`}
key={index}
>
{player.position === 0 ? "G" : null}
</div>
));
};
return (
<div id="teams">
<h1>My APP</h1>
{setTeams(homeTeam)}
{setTeams(awayTeam)}
</div>
);
};
ReactDOM.render(<App/>,document.querySelector("#root"));
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
You're not returning anything in your setPlayers and setTeams functions. You need to return team.map and setPlayers as well like so:
const setTeams = (home, away) => {
return setPlayers = (team) => {
return team.map((player, index) => {
console.log(player);
return (
<div className={`player ${player.team === "home" ? "home" : "away"}`} key={index}>
{player.position === 0 ? "G" : null}
</div>
);
});
};
Solution
const Teams = () => {
const [homeTeam] = useState([
{ position: 0, team: 'home', visible: true, x: '30%', y: '30%' },
{ position: 1, team: 'home', visible: true, x: '40%', y: '30%' }
]);
const [awayTeam] = useState([
{ position: 0, team: 'away', visible: true, x: '70%', y: '70%' },
{ position: 1, team: 'away', visible: true, x: '60%', y: '70%' }
]);
const setPlayers = useCallback(
(team) =>
team.map((player, index) => (
<div className={`player ${player.team === 'home' ? 'home' : 'away'}`} key={index}>
{player.position === 0 ? 'G' : null}
</div>
)),
[]
);
return (
<div>
{setPlayers(homeTeam)}
{setPlayers(awayTeam)}
</div>
);
};
export default Teams;
The react-swipeable-views library is providing example usages. I want to reuse the coverflow example, but in a react functional comp. way. I almost managed to get it working. However on my implementation, the swipeable element can get stuck during swipe, if you swipe slowly (the scale is not applied anymore). See screenshot:
In the demo of react-swipeable views this is somehow not happening. This example is using react-spring for the animation transitions. I provided a stackblitz demo that is reproducable and maybe you can find out the issue.
component
const useStyles = makeStyles((theme) => ({
root: {
background: theme.palette.background.paper,
padding: theme.spacing(0, 6),
},
img: {
width: 180,
height: 180,
display: "block",
marginBottom: theme.spacing(2),
},
container: {
padding: theme.spacing(2),
borderRadius: 4,
justifyContent: "center",
maxWidth: 320,
margin: "auto",
},
slide: {
padding: theme.spacing(3, 2),
color: theme.palette.text.primary,
alignItems: "center",
justifyContent: "center",
flexDirection: "column",
display: "flex",
},
}));
const albums = [
{
name: "Abbey Road",
src: "https://picsum.photos/200/300",
},
{
name: "Bat Out of Hell",
src: "https://picsum.photos/200/300",
},
{
name: "Homogenic",
src: "https://picsum.photos/200/300",
},
{
name: "Number of the Beast",
src: "https://picsum.photos/200/300",
},
{
name: "It's Blitz",
src: "https://picsum.photos/200/300",
},
{
name: "The Man-Machine",
src: "https://picsum.photos/200/300",
},
];
export function StatisticSelector() {
const classes = useStyles();
const [index, setIndex] = useState(0);
const [props, start] = useSpring(() => ({
from: { position: 0 },
}));
function handleChangeIndex(indexNum) {
setIndex(indexNum);
}
function handleSwitch(index, type) {
if (type === "end") {
start({
from: { position: props.position.value },
to: { position: Math.round(index) },
});
return;
}
props.position.setValue(index);
}
function interpolatePositionProps(range, output) {
return props.position.interpolate({
range,
output,
});
}
return (
<div className={classes.container}>
<SwipeableViews
index={index}
className={classes.root}
onChangeIndex={handleChangeIndex}
onSwitching={handleSwitch}
enableMouseEvents
>
{albums.map((album, currentIndex) => {
const inputRange = albums.map((_, i) => i);
const scale = interpolatePositionProps(
inputRange,
inputRange.map((i) => (currentIndex === i ? 1 : 0.7))
).interpolate((x) => `scale(${x})`);
const opacity = interpolatePositionProps(
inputRange,
inputRange.map((i) => (currentIndex === i ? 1 : 0.3))
);
const translateX = interpolatePositionProps(
inputRange,
inputRange.map((i) => (100 / 2) * (i - currentIndex))
).interpolate((x) => `translateX(${x}px)`);
const scaleAndTranslateX = interpolate(
[scale, translateX],
(scale, translateX) => `${scale} ${translateX}`
);
return (
<animated.div
key={String(currentIndex)}
className={classes.slide}
style={Object.assign({
opacity,
transform: scaleAndTranslateX,
})}
>
<img className={classes.img} src={album.src} alt="cover" />
<Button variant="contained" color="primary" size="small">
Select
</Button>
</animated.div>
);
})}
</SwipeableViews>
</div>
);
}
function handleSwitch(index, type) {
if (type === "end") {
start({
from: { position: props.position.value },
to: { position: Math.round(index) }
});
/**
* Solution:
* Do not stop executing this function and make final value update.
* Just comment `return` below, and everything will work as expected.
*/
// return;
}
props.position.setValue(index);
}
I have created a custom tab bar component using react native navigation following this tutorial: https://reactnavigation.org/docs/bottom-tab-navigator#tabbar. I have then placed a custom component on top of the navigation to mimick spotify minimized player so that when I can execute a drag gesture from minimized to a full sized player. However, When I run the app, the gesture works fine on the first screen, but when I switch screens the gesture doesn't work https://www.youtube.com/watch?v=w-8NSQyWPHI&feature=youtu.be.
import React, { Fragment, useState } from 'react';
import { Text, View, TouchableOpacity, Dimensions, StyleSheet } from 'react-native'
import { PanGestureHandler, State } from 'react-native-gesture-handler'
import { clamp, onGestureEvent, timing, withSpring } from 'react-native-redash'
import Animated from 'react-native-reanimated'
import { getBottomSpace } from 'react-native-iphone-x-helper'
import Player from './player/Player'
import MiniPlayer from './player/MiniPlayer'
const { height } = Dimensions.get('window')
const TABBAR_HEIGHT = getBottomSpace() + 50
const MINIMIZED_PLAYER_HEIGHT = 52
const SNAP_TOP = 0
const SNAP_BOTTOM = height - TABBAR_HEIGHT - MINIMIZED_PLAYER_HEIGHT
const config = {
damping: 30,
mass: 1,
stiffness: 150,
overshootClamping: false,
restSpeedThreshold: 0.1,
restDisplacementThreshold: 0.1
}
const {
Clock,
Value,
cond,
useCode,
set,
block,
not,
clockRunning,
interpolate,
diffClamp,
Extrapolate
} = Animated
const styles = StyleSheet.create({
container: {
flex: 1,
},
playerSheet: {
...StyleSheet.absoluteFillObject,
backgroundColor: 'white'
}
})
export default ({ statee, descriptors, navigation }) => {
const translationY = new Value(0)
const velocityY = new Value(0)
const state = new Value(State.UNDETERMINED)
const offset = new Value(SNAP_BOTTOM)
const goUp: Animated.Value<0 | 1> = new Value(0)
const goDown: Animated.Value<0 | 1> = new Value(0)
const gestureHandler = onGestureEvent({
state,
translationY,
velocityY
})
const translateY = clamp(
withSpring({
state,
offset,
value: translationY,
velocity: velocityY,
snapPoints: [SNAP_TOP, SNAP_BOTTOM],
config
}),
SNAP_TOP,
SNAP_BOTTOM
)
const translateBottomTab = interpolate(translateY, {
inputRange: [SNAP_TOP, SNAP_BOTTOM],
outputRange: [TABBAR_HEIGHT, 0],
extrapolate: Extrapolate.CLAMP
})
const opacity = interpolate(translateY, {
inputRange: [SNAP_BOTTOM - MINIMIZED_PLAYER_HEIGHT, SNAP_BOTTOM],
outputRange: [0, 1],
extrapolate: Extrapolate.CLAMP
})
const opacity2 = interpolate(translateY, {
inputRange: [
SNAP_BOTTOM - MINIMIZED_PLAYER_HEIGHT * 2,
SNAP_BOTTOM - MINIMIZED_PLAYER_HEIGHT
],
outputRange: [0, 1],
extrapolate: Extrapolate.CLAMP
})
const clock = new Clock()
useCode(
block([
cond(goUp, [
set(
offset,
timing({
clock,
from: offset,
to: SNAP_TOP
})
),
cond(not(clockRunning(clock)), [set(goUp, 0)])
]),
cond(goDown, [
set(
offset,
timing({
clock,
from: offset,
to: SNAP_BOTTOM
})
),
cond(not(clockRunning(clock)), [set(goDown, 0)])
])
]),
[]
)
goUpHandler = () => {
console.log('TAPPERD UP');
goUp.setValue(1)
}
goDownHandler = () => {
console.log('TAPPERD DOWN');
goDown.setValue(1)
}
return (
<>
<PanGestureHandler {...gestureHandler}>
<Animated.View
style={[styles.playerSheet, { transform: [{ translateY }] }]}
>
<Player onPress={this.goDownHandler} />
<Animated.View
pointerEvents='none'
style={{
opacity: opacity2,
backgroundColor: 'white',
...StyleSheet.absoluteFillObject
}}
/>
<Animated.View style={{
opacity: opacity,
position: 'absolute',
top: 0,
left: 0,
right: 0,
height: MINIMIZED_PLAYER_HEIGHT
}}>
<MiniPlayer onPress={this.goUpHandler} />
</Animated.View>
</Animated.View>
</PanGestureHandler>
<Animated.View style={{
flexDirection: 'row',
height: TABBAR_HEIGHT,
paddingTop: 8,
justifyContent: 'center',
backgroundColor: 'white',
transform: [{ translateY: translateBottomTab }]
}}>
{statee.routes.map((route, index) => {
const { options } = descriptors[route.key];
const label =
options.tabBarLabel !== undefined
? options.tabBarLabel
: options.title !== undefined
? options.title
: route.name;
const Icon = options.tabBarIcon
const isFocused = statee.index === index;
const onPress = () => {
const event = navigation.emit({
type: 'tabPress',
target: route.key,
canPreventDefault: true,
});
if (!isFocused && !event.defaultPrevented) {
navigation.navigate(route.name);
}
};
const onLongPress = () => {
navigation.emit({
type: 'tabLongPress',
target: route.key,
});
};
return (
<Animated.View style={styles.container} key={index}>
<Animated.View style={{
alignItems: 'center',
justifyContent: 'center'
}}>
<TouchableOpacity
accessibilityRole="button"
accessibilityStates={isFocused ? ['selected'] : []}
accessibilityLabel={options.tabBarAccessibilityLabel}
testID={options.tabBarTestID}
onPress={onPress}
onLongPress={onLongPress}
>
<Icon />
</TouchableOpacity>
</Animated.View>
</Animated.View>
);
})}
</Animated.View>
</>
);
}
I have the exact issue. This is because the reanimated is messed up when re-rendering.
I fixed that by moving the below code out of the functional/class component to make it global.
const translationY = new Value(0)
const velocityY = new Value(0)
const state = new Value(State.UNDETERMINED)
const offset = new Value(SNAP_BOTTOM)
const goUp: Animated.Value<0 | 1> = new Value(0)
const goDown: Animated.Value<0 | 1> = new Value(0)
const gestureHandler = onGestureEvent({
state,
translationY,
velocityY
})
const translateY = clamp(
withSpring({
state,
offset,
value: translationY,
velocity: velocityY,
snapPoints: [SNAP_TOP, SNAP_BOTTOM],
config
}),
SNAP_TOP,
SNAP_BOTTOM
)
const translateBottomTab = interpolate(translateY, {
inputRange: [SNAP_TOP, SNAP_BOTTOM],
outputRange: [TABBAR_HEIGHT, 0],
extrapolate: Extrapolate.CLAMP
})
const opacity = interpolate(translateY, {
inputRange: [SNAP_BOTTOM - MINIMIZED_PLAYER_HEIGHT, SNAP_BOTTOM],
outputRange: [0, 1],
extrapolate: Extrapolate.CLAMP
})
const opacity2 = interpolate(translateY, {
inputRange: [
SNAP_BOTTOM - MINIMIZED_PLAYER_HEIGHT * 2,
SNAP_BOTTOM - MINIMIZED_PLAYER_HEIGHT
],
outputRange: [0, 1],
extrapolate: Extrapolate.CLAMP
})
const clock = new Clock()
useCode(
block([
cond(goUp, [
set(
offset,
timing({
clock,
from: offset,
to: SNAP_TOP
})
),
cond(not(clockRunning(clock)), [set(goUp, 0)])
]),
cond(goDown, [
set(
offset,
timing({
clock,
from: offset,
to: SNAP_BOTTOM
})
),
cond(not(clockRunning(clock)), [set(goDown, 0)])
])
]),
[]
)
How do I change one icon into two different icons in flutter? This is what I have so far.
Widget build(BuildContext context) {
final WebPage currentPage = webPages[currentPageIndex];
return Scaffold(
body: SafeArea(
child: WebView(
initialUrl: currentPage.url,
javaScriptMode: JavaScriptMode.unrestricted,
onWebViewCreated: _controller.complete,
),
),
bottomNavigationBar: BottomNavigationBar(
onTap: onTapNavigation,
currentIndex: currentPageIndex,
items: webPages
.map((webPage) => BottomNavigationBarItem(
icon: Icon(Icons.school), title: Text(webPage.title)))
.toList()),
);
}
here is how i once implemented switching icons based on a bool value
bool _newNotification = false;
Tab(
icon: _newNotification
? Icon(
Icons.directions_boat,
size: 28,
color: Colors.white,
),
: Icon(
Icons.notifications,
size: 28,
color: Colors.white,
),
),